This file is used to prepare the figures for the paper.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)
library(org.Mm.eg.db)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

Here are the folders where analyzes are stored :

data_dir = "./.."
list.files(data_dir)
##  [1] "0_intro"      "1_metadata"   "2_individual" "3_combined"   "4_zoom"      
##  [6] "5_wu"         "6_figures"    "LICENSE"      "index.html"   "index_layout"

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/3_combined/hs_hd_sobj.rds"))
sobj
## An object of class Seurat 
## 20003 features across 12111 samples within 1 assay 
## Active assay: RNA (20003 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/1_metadata/hs_hd_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

These are the custom colors for cell populations :

color_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_color_markers.rds"))
color_markers = color_markers[names(color_markers) != "melanocytes"]
ors_color = color_markers["ORS"]
color_markers["ORS"] = color_markers["IFE"] 
color_markers["IFE"] = ors_color
color_markers["B cells"] = "chocolate3"
rm(ors_color)

# re-order
color_markers = color_markers[c("CD4 T cells", "CD8 T cells", "Langerhans cells", "macrophages", "B cells",
                                "cuticle", "cortex", "medulla", "IRS", "proliferative",
                                "HFSC", "ORS", "IBL", "IFE", "sebocytes")]

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(hjust = 1, angle = 20))

We define custom colors for sample type :

sample_type_colors = setNames(nm = levels(sample_info$sample_type),
                              c("#C55F40", "#2C78E6"))

data.frame(cell_type = names(sample_type_colors),
           color = unlist(sample_type_colors)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(sample_type_colors), breaks = names(sample_type_colors)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank())

We set a background color :

bg_color = "gray94"

This is the correspondence between cell types and cell families, and custom colors to color cells by cell category :

custom_order_cell_type = data.frame(
  cell_type = names(color_markers),
  cell_category = c(rep("immune cells", 5),
                    rep("matrix", 5),
                    rep("non matrix", 5)),
  stringsAsFactors = FALSE)
custom_order_cell_type$cell_type = factor(custom_order_cell_type$cell_type,
                                          levels = custom_order_cell_type$cell_type)
rownames(custom_order_cell_type) = custom_order_cell_type$cell_type

category_color = c("immune cells" = "slateblue1",
                   "matrix" = "mediumseagreen",
                   "non matrix" = "firebrick3")

We load markers to display on a dotplot to assess cell type annotation :

dotplot_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_dotplot_markers.rds"))
dotplot_markers = dotplot_markers[names(dotplot_markers) != "melanocytes"]
lengths(dotplot_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##                2                2                2                2 
##          B cells          cuticle           cortex          medulla 
##                2                2                2                2 
##              IRS    proliferative              IBL              ORS 
##                2                2                2                2 
##              IFE             HFSC        sebocytes 
##                2                2                2

Custom functions to display gene expression on the heatmap :

color_fun = function(one_gene) {
  gene_range = range(ht_annot[, one_gene])
  gene_palette = circlize::colorRamp2(colors = c("#FFFFFF", aquarius::color_gene[-1]),
                                      breaks = seq(from = gene_range[1], to = gene_range[2],
                                                   length.out = length(aquarius::color_gene)))
  return(gene_palette)
}

All samples

Settings

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

Preparation

We make a low resolutive clustering for the heatmap :

sobj = Seurat::FindClusters(sobj, resolution = 0.4)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 12111
## Number of edges: 475472
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.9421
## Number of communities: 17
## Elapsed time: 1 seconds
length(levels(sobj$seurat_clusters))
## [1] 17

We define cluster type and cluster category :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type))
sobj$cluster_category = custom_order_cell_type[sobj$cluster_type, "cell_category"]
sobj$cluster_category = factor(sobj$cluster_category,
                               levels = names(category_color))

Figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Sample type :

# Extract coordinates
cells_coord = sobj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj$sample_type
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

grey_palette = setNames(nm = levels(sobj$seurat_clusters),
                        rep("#D9D9D9", length(levels(sobj$seurat_clusters))))
grey_palette[c("7", "16", "1", "12", "11", "10", "15")] = "#BDBDBD"
grey_palette[c("16", "14", "5", "9")] = "#969696"

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.4,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 6) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster category annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_category", cols = category_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cell type annotation split by condition :

plot_list = aquarius::plot_split_dimred(sobj, reduction = name2D,
                                        group_by = "cell_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        split_color = sample_type_colors,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14",
          # For supplementary figure
          "VIM", "FGF18", "SOX9", "TCF3", "TBX1",
          "RUNX1", "NFATC1", "LHX2", "CD34")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  p = Seurat::FeaturePlot(sobj, features = gene, reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = c(0:6)) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15)) +
    Seurat::NoAxes()
  
  return(p)
})

names(plot_list) = genes
plot_list
## $PTPRC

## 
## $MSX2

## 
## $KRT14

## 
## $VIM

## 
## $FGF18

## 
## $SOX9

## 
## $TCF3

## 
## $TBX1

## 
## $RUNX1

## 
## $NFATC1

## 
## $LHX2

## 
## $CD34

Barplot by cluster category :

quantif = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

aquarius::plot_barplot(df = table(sobj$sample_identifier,
                                  sobj$cluster_category) %>%
                         as.data.frame.table() %>%
                         `colnames<-`(c("Sample", "Cell Type", "Number")),
                       x = "Sample", y = "Number", fill = "Cell Type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 4) +
  ggplot2::scale_fill_manual(values = unlist(category_color),
                             breaks = names(category_color),
                             name = "Cell category") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 axis.text.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0)),
                 legend.position = "none")

Heatmap of cluster proportion by sample :

group_by = "seurat_clusters"

cluster_by_sample = table(sobj$sample_identifier,
                          sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

## Right annotation : number of cells by dataset
ht_annot = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Left annotation : gender
ha_left = ComplexHeatmap::HeatmapAnnotation(
  gender = sample_info$gender,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "top",
  col = list(gender = setNames(nm = c("F", "M"),
                               c("lightcyan3", "navyblue"))))

## Top annotation : main cell type in this cluster
ht_annot = table(sobj$sample_identifier,
                 sobj@meta.data[, group_by]) %>%
  prop.table(margin = 1) %>%
  as.matrix()

ht_annot = table(sobj$cell_type,
                 sobj@meta.data[, group_by]) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
ht_annot = data.frame(row.names = names(ht_annot),
                      cell_type = names(color_markers)[ht_annot],
                      stringsAsFactors = FALSE)
ht_annot = dplyr::left_join(ht_annot, custom_order_cell_type, by = "cell_type") %>%
  # Simplification for matrix
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("medulla", "cortex", "cuticle"), yes = "hair shaft", no = cell_type)) %>%
  # Simplification for T cells
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("CD4 T cells", "CD8 T cells"), yes = "T cells", no = cell_type)) %>%
  # Simplification for APC
  dplyr::mutate(cell_type = ifelse(cell_type %in% c("Langerhans cells", "macrophages"), yes = "APC", no = cell_type)) %>%
  # Add color
  dplyr::mutate(color = as.character(color_markers[cell_type])) %>%
  dplyr::mutate(color = ifelse(cell_type == "hair shaft", yes = "#FFB6C1", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "T cells", yes = "#8A6EE6", no = color)) %>%
  dplyr::mutate(color = ifelse(cell_type == "APC", yes = "#9CAA4B", no = color))

ha_top = ComplexHeatmap::HeatmapAnnotation(
  # cell_type = ht_annot$cell_type,
  cell_category = ht_annot$cell_category,
  which = "column",
  show_legend = TRUE,
  show_annotation_name = FALSE,
  # annotation_name_side = "left",
  col = list(#cell_type = setNames(nm = ht_annot$cell_type,
    #                      ht_annot$color),
    cell_category = category_color
  ))

## Assemble heatmap
ht = ComplexHeatmap::Heatmap(cluster_by_sample,
                             heatmap_legend_param = list(title = "Proportion",
                                                         col = c("#2166AC", "#F7F7F7", "#B2182B")),
                             # bottom_annotation = ha_bottom,
                             right_annotation = ha_right,
                             left_annotation = ha_left,
                             top_annotation = ha_top,
                             cluster_rows = TRUE,
                             cluster_columns = TRUE,
                             row_title = "Sample",
                             row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                       col = sample_info$color,
                                                       fontface = "bold"),
                             column_title = "Cluster",
                             column_names_centered = TRUE,
                             row_names_side = "left",
                             column_names_side = "top",
                             column_names_rot = 0)

## Draw !
ComplexHeatmap::draw(ht, merge_legends = TRUE)

For the dotplot, we clarify clusters and cell type annotation :

cell_type_in_cluster = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 1) %>%
  apply(., 1, which.max)
cell_type_in_cluster = cell_type_in_cluster - 1

missing_cluster = setdiff(levels(sobj$seurat_clusters),
                          cell_type_in_cluster)

cell_type_in_cluster = data.frame(cell_type = c(names(cell_type_in_cluster), cluster_type[missing_cluster]),
                                  cluster_id = c(cell_type_in_cluster, names(cluster_type[missing_cluster])),
                                  stringsAsFactors = FALSE, row.names = NULL) %>%
  dplyr::mutate(cluster_id = as.numeric(cluster_id)) %>%
  dplyr::arrange(cell_type, cluster_id)

custom_order_cell_type$clusters = custom_order_cell_type %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    cell_type = one_row["cell_type"]
    clusters = cell_type_in_cluster %>%
      dplyr::filter(.data$cell_type == .env$cell_type) %>%
      dplyr::pull(cluster_id)
    
    cell_type_cluster = paste0(cell_type, " (", paste0(clusters, collapse = ", "), ")")
    
    return(cell_type_cluster)
  }) %>%
  factor(., levels = .)

custom_order_cell_type
##                         cell_type cell_category                     clusters
## CD4 T cells           CD4 T cells  immune cells              CD4 T cells (2)
## CD8 T cells           CD8 T cells  immune cells          CD8 T cells (2, 14)
## Langerhans cells Langerhans cells  immune cells         Langerhans cells (6)
## macrophages           macrophages  immune cells              macrophages (6)
## B cells                   B cells  immune cells                 B cells (16)
## cuticle                   cuticle        matrix                  cuticle (3)
## cortex                     cortex        matrix                   cortex (3)
## medulla                   medulla        matrix                 medulla (11)
## IRS                           IRS        matrix                     IRS (15)
## proliferative       proliferative        matrix proliferative (4, 9, 10, 13)
## HFSC                         HFSC    non matrix                  HFSC (7, 8)
## ORS                           ORS    non matrix                      ORS (0)
## IBL                           IBL    non matrix                      IBL (1)
## IFE                           IFE    non matrix                      IFE (5)
## sebocytes               sebocytes    non matrix               sebocytes (12)

Dotplot :

plot_list = aquarius::plot_dotplot(sobj,
                                   markers = c("PTPRC",
                                               "CD3E", "CD4",
                                               "CD3E", "CD8A",
                                               "CD207", "AIF1",
                                               "TREM2", "MSR1",
                                               "CD79A", "CD79B",
                                               # "PRDM1", "KRT85",
                                               "MSX2",
                                               "KRT32", "KRT35",
                                               "KRT31", "PRR9",
                                               "BAMBI", "ALDH1A3",
                                               "KRT71", "KRT73",
                                               "TOP2A", "MCM5",
                                               "KRT14", "CXCL14",
                                               "KRT15", "COL17A1",
                                               "DIO2", "TCEAL2",
                                               "KRT16", "KRT6C",
                                               "SPINK5", "LY6D",
                                               "CLMP", "PPARG"),
                                   assay = "RNA", column_name = "cell_type", nb_hline = 0) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "left",
                 legend.justification = "bottom",
                 legend.box = "vertical",
                 legend.box.margin = margin(0,70,0,0),
                 axis.title = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.text.x = element_blank(),
                 axis.line.x = element_blank(),
                 plot.margin = unit(rep(0, 4), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(x = clusters, y = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(x = 0.5, xend = 5.5, y = 0, yend = 0), size = 6, col = category_color["immune cells"]) +
  ggplot2::geom_segment(aes(x = 5.5, xend = 10.5, y = 0, yend = 0), size = 6, col = category_color["matrix"]) +
  ggplot2::geom_segment(aes(x = 10.5, xend = 15.5, y = 0, yend = 0), size = 6, col = category_color["non matrix"]) +
  ggplot2::scale_y_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.y = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.title = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
                 plot.margin = unit(c(0,0.5,0.5,0), "cm"))

plot_list = patchwork::wrap_plots(plot_list, p,
                                  ncol = 1, heights = c(25, 1))
plot_list

Immune cells

Settings

We load the immune cells dataset :

sobj_ic = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_sobj.rds"))
sobj_ic
## An object of class Seurat 
## 15121 features across 2329 samples within 1 assay 
## Active assay: RNA (15121 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/1_zoom_immune/immune_cells_list_results.rds"))

lapply(list_results, FUN = names)
## $`Langerhans cells`
## [1] "mark" "gsea"
## 
## $macrophages
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD4 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $`CD8 T cells`
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type and cluster category :

cluster_type = table(sobj_ic$cell_type, sobj_ic$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_ic$cell_type)[cluster_type])

sobj_ic$cluster_type = cluster_type[sobj_ic$seurat_clusters]
sobj_ic$cluster_type = factor(sobj_ic$cluster_type,
                              levels = levels(sobj_ic$cell_type))

Figures

Immune cells on the full atlas :

sobj$is_immune = (colnames(sobj) %in% colnames(sobj_ic))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_immune", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(category_color[["immune cells"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "Immune cells",
                subtitle = paste0(ncol(sobj_ic), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_ic, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Clustering

Seurat::DimPlot(sobj_ic, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", label = TRUE) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Table to make violin plot by sample, by sample type, and feature plot split by sample type :

list_clusters = list(all = names(cluster_type),
                     macrophages = 2,
                     CD4_T_cells = names(cluster_type[cluster_type == "CD4 T cells"]),
                     CD8_T_cells = names(cluster_type[cluster_type == "CD8 T cells"]))
list_subsobj = lapply(list_clusters, FUN = function(clusters) {
  sobj_ic$seurat_clusters = as.character(sobj_ic$seurat_clusters)
  
  # Extract cells of interest
  subsobj = subset(sobj_ic, seurat_clusters %in% clusters)
  
  return(subsobj)
})
names(list_subsobj) = names(list_clusters)
rm(list_clusters)

table_ic = data.frame(clusters = c("all", rep("macrophages", 2),
                                   rep("CD4_T_cells", 3), rep("CD8_T_cells", 2)),
                      feature = c("IL6", "IL1B", "TNF", "GZMA", "IFNG", "IL17A", "PRF1", "GZMB"))
table_ic
##      clusters feature
## 1         all     IL6
## 2 macrophages    IL1B
## 3 macrophages     TNF
## 4 CD4_T_cells    GZMA
## 5 CD4_T_cells    IFNG
## 6 CD4_T_cells   IL17A
## 7 CD8_T_cells    PRF1
## 8 CD8_T_cells    GZMB

Violin plots split by sample :

plot_list = apply(table_ic, 1, FUN = function(one_row) {
  # Input
  population = one_row[1]
  subsobj = list_subsobj[[population]]
  feature = one_row[2]
  
  # Make figure
  p = Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                      features = feature, cols = sample_info$color) +
    ggplot2::labs(subtitle = population) +
    ggplot2::theme(plot.subtitle = element_text(hjust = 0.5),
                   axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = table_ic$feature
plot_list
## $IL6

## 
## $IL1B

## 
## $TNF

## 
## $GZMA

## 
## $IFNG

## 
## $IL17A

## 
## $PRF1

## 
## $GZMB

Feature plots split by sample type :

plot_list = lapply(table_ic$feature, FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_ic,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

names(plot_list) = table_ic$feature
plot_list
## $IL6

## 
## $IL1B

## 
## $TNF

## 
## $GZMA

## 
## $IFNG

## 
## $IL17A

## 
## $PRF1

## 
## $GZMB

Violin plots split by sample type :

plot_list = apply(table_ic, 1, FUN = function(one_row) {
  # Input
  population = one_row[1]
  subsobj = list_subsobj[[population]]
  feature = one_row[2]
  
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                      features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    ggplot2::labs(subtitle = population) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(plot.subtitle = element_text(hjust = 0.5),
                   axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = table_ic$feature
plot_list
## $IL6

## 
## $IL1B

## 
## $TNF

## 
## $GZMA

## 
## $IFNG

## 
## $IL17A

## 
## $PRF1

## 
## $GZMB

Barplot by cluster type :

quantif_total = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

# Number of cytotoxic T cells
quantif_cytotox = sobj_ic@meta.data %>%
  dplyr::mutate(cytotoxic = ifelse(seurat_clusters %in% c("5","6"),
                                   yes = "Cytotoxic CD8 T cells",
                                   no = as.character(cluster_type))) %>%
  # cells of interest
  dplyr::filter(cytotoxic == "Cytotoxic CD8 T cells") %>%
  # count by sample
  dplyr::select(sample_identifier, cluster_type) %>%
  base::droplevels() %>%
  table() %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(isCytotoxic = "cytotoxic")
# Number of immune cells
quantif_immune = table(sobj_ic$sample_identifier, sobj_ic$cluster_type) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::filter(CellType %in% c("CD4 T cells", "CD8 T cells", "macrophages")) %>%
  dplyr::mutate(isCytotoxic = "no") %>%
  # join cytotoxic
  rbind(., quantif_cytotox) %>%
  # proportion for the whole dataset
  dplyr::left_join(., quantif_total, by = "Sample") %>%
  dplyr::mutate(prop_cells = 100*round(Number/nb_cells,4)) %>%
  `colnames<-`(c("Sample", "CellType", "Number", "isCytotoxic", "Total", "Proportion")) %>%
  dplyr::mutate(isCytotoxic = factor(isCytotoxic, levels = c("cytotoxic", "no")))

ggplot2::ggplot(data = quantif_immune %>% dplyr::filter(isCytotoxic != "cytotoxic"),
                aes(x = Sample, y = Proportion, fill = CellType)) +
  ggplot2::geom_bar(stat = "identity", position = position_dodge(width = 0.5),
                    color = "black", size = 0.25, width = 0.4) +
  ggplot2::scale_fill_manual(values = unlist(color_markers[c("CD4 T cells", "CD8 T cells", "macrophages")]),
                             breaks = names(color_markers[c("CD4 T cells", "CD8 T cells", "macrophages")]),
                             name = "Cell Type") +
  # Overlay a layer for stripe
  # ggplot2::geom_bar(data = quantif_immune %>% dplyr::filter(isCytotoxic == "cytotoxic"),
  #                   stat = "identity", position = position_dodge(width = 0.5), fill = "black",
  #                   color = "lightgray", size = 0.25, width = 0.4/3, aes(pattern = isCytotoxic)) +
  # ggpattern::geom_col_pattern(data = quantif_immune %>% dplyr::filter(isCytotoxic == "cytotoxic"),
  #                             pattern_density = 0.001,
  #                             pattern_spacing = 0.015,
  #                             pattern_key_scale_factor = 1,
  #                             color = "black", size = 0.25,
  #                             width = 0.37/3,
  #                             pattern_color = "black", # bar border
#                             show.legend = TRUE) +
# ggpattern::scale_pattern_manual(name = "CD8 T cells", values =  "stripe") +
# ggpattern::scale_pattern_angle_manual(name = "CD8 T cells", values = 50) +
# ggplot2::guides(pattern = ggplot2::guide_legend(override.aes = list(fill = "white")),
#                 fill = ggplot2::guide_legend(override.aes = list(pattern = "none"))) +
# Theme
ggplot2::scale_y_continuous(limits = c(0, max(quantif_immune$Proportion)),
                            expand = ggplot2::expansion(add = c(0, 1))) +
  ggplot2::labs(y = "% of immune cells by sample") +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 axis.text.x = element_text(angle = 0, hjust = 0.5),
                 panel.grid.major.y = element_line(color = "lightgray", size = 0.5),
                 panel.grid.minor.y = element_line(color = "lightgray", size = 0.5))

Heatmap for macrophages :

subsobj = subset(sobj_ic, cluster_type == "macrophages")
features_oi = c("IL1B", "TNF",
                "HLA-DQA2", "HLA-DPA1", "HLA-DRB5",
                "HLA-A", "HLA-C", "B2M",
                "C1QA", "C1QB", "C1QC")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  11 463
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Heatmap for CD4 T cells :

subsobj = subset(sobj_ic, cluster_type == "CD4 T cells")
features_oi = c("GZMA", "KLRB1", "BTG1", "ZFP36", "NFKBIA", "TXNIP", "CXCR4", "IFNG", "IL17A")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   9 848
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "left",
                     annotation_legend_side = "left")

HFSC

Settings

We load the HFSCs dataset :

sobj_hfsc = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_sobj.rds"))
sobj_hfsc
## An object of class Seurat 
## 15384 features across 1454 samples within 1 assay 
## Active assay: RNA (15384 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_24_tsne, RNA_pca_24_umap, harmony, harmony_24_umap, harmony_24_tsne

This is the projection name to visualize cells :

name2D = "harmony_24_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/2_zoom_hfsc/hfsc_list_results.rds"))

lapply(list_results, FUN = names)
## $cluster_0_8
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_2
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_1
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"
## 
## $cluster_3
## [1] "p_val"     "avg_logFC" "pct.1"     "pct.2"     "p_val_adj"

Figures

HFSCs on the full atlas :

sobj$is_hfsc = (colnames(sobj) %in% colnames(sobj_hfsc))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_hfsc", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[["HFSC"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "HFSCs",
                subtitle = paste0(ncol(sobj_hfsc), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

KRT15 expression :

Seurat::FeaturePlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                    features = "KRT15") +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Genes of interest :

genes = c("TGFB2", "ANGPTL7", "FGF18", "MGP", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_hfsc$my_gene = Seurat::FetchData(sobj_hfsc, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_hfsc, features = "my_gene", reduction = name2D) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_hfsc), replace = FALSE, size = ncol(sobj_hfsc))

# Extract coordinates
cells_coord = sobj_hfsc@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_hfsc$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_hfsc, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 7) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Heatmap with proportions :

cluster_markers = c("TGFB2", "ANGPTL7", "EPCAM", "KRT75", "NOTCH3", "PTHLH")

## Bottom annotation : gene expression by cluster
ht_annot = Seurat::FetchData(sobj_hfsc, slot = "data", vars = cluster_markers) %>%
  as.data.frame()
ht_annot$clusters = sobj_hfsc$seurat_clusters
ht_annot = ht_annot %>%
  dplyr::group_by(clusters) %>%
  dplyr::summarise_all(funs('mean' = mean)) %>%
  as.data.frame() %>%
  dplyr::select(-clusters) %>%
  `colnames<-`(c(cluster_markers))

ha_bottom = ComplexHeatmap::HeatmapAnnotation(df = ht_annot,
                                              which = "column",
                                              show_legend = TRUE,
                                              col = setNames(nm = cluster_markers,
                                                             lapply(cluster_markers, FUN = color_fun)),
                                              annotation_name_side = "left")

## Right annotation : number of cells by dataset
ht_annot = table(sobj_hfsc$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  `rownames<-`(.$sample_identifier) %>%
  dplyr::select(-sample_identifier)

ha_right = ComplexHeatmap::HeatmapAnnotation(
  df = ht_annot,
  which = "row",
  show_legend = TRUE,
  annotation_name_side = "bottom",
  col = list(nb_cells  = circlize::colorRamp2(colors = RColorBrewer::brewer.pal(name = "Greys", n = 9),
                                              breaks = seq(from = range(ht_annot$nb_cells)[1],
                                                           to = range(ht_annot$nb_cells)[2],
                                                           length.out = 9))))

## Heatmap
ht = aquarius::plot_prop_heatmap(df = sobj_hfsc@meta.data[, c("sample_identifier", "seurat_clusters")],
                                 bottom_annotation = ha_bottom,
                                 # right_annotation = ha_right,
                                 cluster_rows = TRUE,
                                 column_names_centered = TRUE,
                                 prop_margin = 1,
                                 row_names_gp = grid::gpar(names = sample_info$sample_identifier,
                                                           col = sample_info$color,
                                                           fontface = "bold"),
                                 row_title = "Sample",
                                 column_title = "Cluster")

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom")

Heatmap for cluster 0 and 8 :

subsobj = subset(sobj_hfsc, seurat_clusters %in% c(0,8))

features_oi = rownames(list_results$cluster_0_8)
features_oi = features_oi[!grepl(features_oi, pattern = "^RP")]

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = cbind(mat_expr, subsobj$percent.mt)
colnames(mat_expr)[ncol(mat_expr)] = "percent.rb"
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]  48 631
## Colors
list_colors = list()

# Heatmap
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))

# Sample annotation (top annotation)
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
# list_colors[["seurat_clusters"]] = setNames(aquarius::gg_color_hue(length(levels(subsobj$seurat_clusters))),
#                                             nm = levels(subsobj$seurat_clusters))
# Cells order
column_order = subsobj@meta.data %>%
  dplyr::arrange(sample_type, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Heatmap
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           # clusters = subsobj$seurat_clusters,
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]]
                                      # clusters = list_colors[["seurat_clusters"]]
                           ))


# g1 : REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM
# g2 : GOBP_APOPTOTIC_PROCESS
g1_genes = c("B2M", "HLA-C", "HLA-A", "MIF", "PPIA", "JUNB", "IFITM3")
g2_genes = c("Jun", "ATF3", "BTG2", "RHOB", "NFKBIA", "SGK1", "KLF9",
             "CAV1", "DDIT4", "PDK4", "TXNIP", "RNF1152", "TLE1")
ha_right = data.frame(genes =  c(features_oi, "percent.rb"), rownames = c(features_oi, "percent.rb"))
ha_right$group = case_when(ha_right$genes %in% g1_genes ~ "REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM",
                           ha_right$genes %in% g2_genes ~ "GOBP_APOPTOTIC_PROCESS",
                           TRUE ~ "others")

list_colors[["group"]] = setNames(
  nm = c("REACTOME_CYTOKINE_SIGNALING_IN_IMMUNE_SYSTEM", "GOBP_APOPTOTIC_PROCESS", "others"),
  c("red", "black", "gray90"))

ha_right = HeatmapAnnotation(group = ha_right$group,
                             col = list(group = list_colors[["group"]]),
                             which = "row",
                             show_annotation_name = FALSE,
                             show_legend = TRUE)

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             right_annotation = ha_right,
             show_column_names = FALSE,
             column_order = column_order,
             column_gap = unit(2, "mm"),
             cluster_rows = FALSE,
             row_title = NULL,
             row_names_gp = grid::gpar(fontsize = 10, fontface = "plain"),
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Violin plots split by sample :

features_oi = c("IFITM3", "DDIT4")

plot_list = lapply(features_oi, FUN = function(feature) {
  p = Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                      features = feature, cols = sample_info$color) +
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $IFITM3

## 
## $DDIT4

Feature plots split by sample type :

plot_list = lapply(features_oi, FUN = function(one_gene) {
  p = aquarius::plot_split_dimred(sobj_hfsc,
                                  reduction = name2D,
                                  split_by = "sample_type",
                                  color_by = one_gene,
                                  color_palette = c("gray70", "#FDBB84", "#EF6548", "#7F0000", "black"),
                                  main_pt_size = 0.6,
                                  bg_pt_size = 0.6,
                                  order = TRUE,
                                  bg_color = "gray95")
  p = patchwork::wrap_plots(p, nrow = 1) +
    patchwork::plot_layout(guides = "collect") +
    ggplot2::theme(legend.position = "right") &
    ggplot2::theme(plot.subtitle = element_blank())
  return(p)
})

names(plot_list) = features_oi
plot_list
## $IFITM3

## 
## $DDIT4

Violin plots split by sample type :

plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                      features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $IFITM3

## 
## $DDIT4

Barplot with number of HFSCs and total number of cells :

quantif = dplyr::left_join(
  x = table(sobj$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_cells")),
  y = table(sobj_hfsc$sample_identifier) %>%
    as.data.frame.table() %>%
    `colnames<-`(c("Sample", "nb_hfsc")),
  by = "Sample") %>%
  dplyr::mutate(prop_hfsc = round(100*nb_hfsc / nb_cells, 2))

quantif_to_plot = rbind.data.frame(
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_cells - quantif$nb_hfsc,
             cell_type = "others",
             stringsAsFactors = FALSE),
  data.frame(Sample = quantif$Sample,
             nb_cells = quantif$nb_hfsc,
             cell_type = "hfsc",
             stringsAsFactors = FALSE)) %>%
  dplyr::mutate(cell_type = factor(cell_type, levels = c("others", "hfsc")))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "nb_cells", fill = "cell_type",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 0.05+.data$prop_hfsc/100, label = .data$nb_hfsc),
                      label.size = 0, size = 5, fill = NA) +
  ggplot2::scale_fill_manual(values = c("gray90", color_markers[["HFSC"]]),
                             breaks = c("others", "hfsc"),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "none")

IBL and ORS

Settings

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 16701 features across 3532 samples within 1 assay 
## Active assay: RNA (16701 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/3_zoom_iblmors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"       
## 
## $cluster5_vs_ORS
## [1] "mark"       "enrichr_up" "enrichr_dn" "gsea"      
## 
## $IBL_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $ORS_HS_vs_HD
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

Figures

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::labs(title = "IBL + ORS",
                subtitle = paste0(ncol(sobj_iblors), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_iblors), replace = FALSE, size = ncol(sobj_iblors))

# Extract coordinates
cells_coord = sobj_iblors@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj_iblors$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", cols = grey_palette,
                label = TRUE, label.size = 8) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type split by sample type :

plot_list = aquarius::plot_split_dimred(sobj_iblors, reduction = name2D,
                                        group_by = "cluster_type",
                                        group_color = color_markers,
                                        split_by = "sample_type",
                                        bg_pt_size = 1, main_pt_size = 1,
                                        bg_color = bg_color)

patchwork::wrap_plots(plot_list) &
  Seurat::NoLegend()

Number of cells by cluster and by sample :

table(sobj_iblors$seurat_clusters,
      sobj_iblors$sample_identifier)
##     
##      HS_1 HS_2 HS_3 HS_4 HS_5 HD_1 HD_2
##   0    49   15   18  354   82   68   83
##   1    14    0   28  205   49   68   74
##   2    67   17  191   19   38   41   29
##   3    40   18    2  204   24   39   57
##   4    14    2   76   18  215    1   36
##   5    84   83    1   69   28   20   17
##   6    40    9  108   23   76   26   20
##   7    27    7   51   90   21   10   23
##   8    28    7   77   11   20   28   13
##   9    11    4   41   11   73   10    8
##   10   16    3   37    6   26   10    4
table(sobj_iblors$cluster_type,
      sobj_iblors$sample_identifier)
##      
##       HS_1 HS_2 HS_3 HS_4 HS_5 HD_1 HD_2
##   IBL  176   42  530   88  448  116  110
##   ORS  214  123  100  922  204  205  254

Separate cluster 5 :

sobj_iblors$cluster_type_sep5 = ifelse(sobj_iblors$seurat_clusters == 5,
                                       yes = "ORS_5",
                                       no = as.character(sobj_iblors$cluster_type)) %>%
  as.factor()

table(sobj_iblors$cluster_type_sep5,
      sobj_iblors$sample_type)
##        
##           HS   HD
##   IBL   1284  226
##   ORS   1298  422
##   ORS_5  265   37

Barplot by cluster category :

quantif = table(sobj_iblors$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

quantif_to_plot = table(sobj_iblors$sample_identifier,
                        sobj_iblors$cluster_type_sep5) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(Style = ifelse(CellType == "ORS_5", yes = "IL1R2+", no = "IL1R2-")) %>%
  dplyr::mutate(Style = factor(Style, levels = c("IL1R2-", "IL1R2+"))) %>%
  dplyr::mutate(CellType = ifelse(CellType == "ORS_5", yes = "ORS", no = as.character(CellType))) %>%
  `colnames<-`(c("Sample", "Cell Type", "Number", "IL1R2 status"))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "Number",
                       fill = "Cell Type", pattern = "IL1R2 status",
                       position = position_fill()) +
  ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
                      aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
                      label.size = 0, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers[levels(sobj_iblors$cluster_type)]),
                             breaks = names(color_markers[levels(sobj_iblors$cluster_type)]),
                             name = "Cell Type") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "right")

Focus on cluster 5, among ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")
subsobj$cluster_type_sep5 = ifelse(subsobj$seurat_clusters == 5,
                                   yes = "ORS_5",
                                   no = as.character(subsobj$cluster_type)) %>%
  as.factor()

quantif = table(subsobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

quantif_to_plot = table(subsobj$sample_identifier,
                        subsobj$cluster_type_sep5) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  dplyr::mutate(Style = ifelse(CellType == "ORS_5", yes = "IL1R2+", no = "IL1R2-")) %>%
  dplyr::mutate(Style = factor(Style, levels = c("IL1R2-", "IL1R2+"))) %>%
  dplyr::mutate(CellType = ifelse(CellType == "ORS_5", yes = "ORS", no = as.character(CellType))) %>%
  `colnames<-`(c("Sample", "Cell Type", "Number", "IL1R2 status")) %>%
  dplyr::left_join(., y = quantif, by = "Sample") %>%
  dplyr::mutate(prop_cluster5 = round(Number/nb_cells, 4))

aquarius::plot_barplot(df = quantif_to_plot,
                       x = "Sample", y = "Number",
                       fill = "IL1R2 status",
                       position = position_fill()) +
  # ggplot2::geom_label(data = quantif, inherit.aes = FALSE,
  #                     aes(x = .data$Sample, y = 1.05, label = .data$nb_cells),
  #                     label.size = 0, size = 5) +
  ggplot2::geom_label(data = quantif_to_plot %>%
                        dplyr::filter(`IL1R2 status` == "IL1R2+"),
                      inherit.aes = FALSE,
                      aes(x = .data$Sample, y = prop_cluster5 + 0.05, label = .data$Number),
                      label.size = 0, size = 5, fill = NA, col = "white") +
  ggplot2::scale_fill_manual(values = c("black", color_markers[["ORS"]]),
                             breaks = c("IL1R2+", "IL1R2-"),
                             name = "IL1R2 status") +
  ggplot2::scale_y_continuous(breaks = seq(0, 1, by = 0.25),
                              labels = paste0(seq(0, 100, by = 25), sep = " %"),
                              expand = ggplot2::expansion(add = c(0, 0.05))) +
  ggplot2::theme(axis.title.y = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 legend.position = "right")

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Score for both gene sets, in all cells :

significant = function(pval) {
  signif = case_when(pval > 0.05 ~ "ns",
                     pval > 0.01 & pval <= 0.05 ~ "*",
                     pval > 0.001 & pval <= 0.01 ~ "**",
                     pval <= 0.001 ~ "***")
  return(signif)
}

plot_vln_score = function(score_name, the_gs_name) {
  # t-test between sample type
  feature_expr = sobj_iblors@meta.data[, score_name]
  
  ## in mORS
  feature_hs = feature_expr[sobj_iblors$sample_type == "HS" & sobj_iblors$cluster_type == "ORS"]
  feature_hd = feature_expr[sobj_iblors$sample_type == "HD" & sobj_iblors$cluster_type == "ORS"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  pval_ors = feature_hs_VS_feature_hd$p.value
  
  ## in IBL
  feature_hs = feature_expr[sobj_iblors$sample_type == "HS" & sobj_iblors$cluster_type == "IBL"]
  feature_hd = feature_expr[sobj_iblors$sample_type == "HD" & sobj_iblors$cluster_type == "IBL"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  pval_ibl = feature_hs_VS_feature_hd$p.value
  
  # Significance associated with p-value
  significance_ors = significant(pval_ors)
  significance_ibl = significant(pval_ibl)
  
  # Plot
  p = Seurat::VlnPlot(sobj_iblors, features = score_name, pt.size = 0.05,
                      split.by = "sample_type", group.by = "cluster_type") +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar (IBL)
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 0.75, xend = 1.25,
                          y = max(feature_expr)*1.2, yend = max(feature_expr)*1.2) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1, y = max(feature_expr)*1.25,
                        label = significance_ibl,
                        fill = NA, label.size = 0) +
    # Significance bar (mORS)
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1.75, xend = 2.25,
                          y = max(feature_expr)*1.2, yend = max(feature_expr)*1.2) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 2, y = max(feature_expr)*1.25,
                        label = significance_ors,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, 1.3*max(feature_expr))) +
    # Style
    ggplot2::labs(title = the_gs_name) +
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
}
gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")

# Genes in the gene set of interest
the_gs_name = "REACTOME_KERATINIZATION"
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

# Attribute a score
sobj_iblors$score_kera = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

# Violin plot
plot_vln_score("score_kera", the_gs_name)

# Genes in the gene set of interest
the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE"
the_gs_content = gene_sets$gene_sets_full %>%
  dplyr::filter(gs_name == the_gs_name) %>%
  dplyr::pull(gene_symbol) %>%
  unlist()

# Attribute a score
sobj_iblors$score_ifng = Seurat::AddModuleScore(sobj_iblors,
                                                features = list(the_gs_content))$Cluster1

# Violin plot
plot_vln_score("score_ifng", the_gs_name)

Violin plot for IBL :

subsobj = subset(sobj_iblors, cluster_type == "IBL")
features_oi = c("DUSP1", "DDIT4", "MIF", "LGALS7", "ARF5", "S100A9")

# Plot !
plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                      features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $DUSP1

## 
## $DDIT4

## 
## $MIF

## 
## $LGALS7

## 
## $ARF5

## 
## $S100A9

Violin plot for ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")
features_oi = c("DUSP1", "KLF6", "CLDN1", "CTGF",
                "S100A9", "CCL2", "IFITM3", "IFI27")

# Plot !
plot_list = lapply(features_oi, FUN = function(feature) {
  # t-test between sample type
  feature_expr = subsobj@assays$RNA@data[feature, ]
  feature_hs = feature_expr[subsobj$sample_type == "HS"]
  feature_hd = feature_expr[subsobj$sample_type == "HD"]
  feature_hs_VS_feature_hd = stats::t.test(feature_hs, feature_hd)
  feature_hs_VS_feature_hd
  
  # Significance associated with p-value
  pval = feature_hs_VS_feature_hd$p.value
  
  significance = case_when(pval > 0.05 ~ "ns",
                           pval > 0.01 & pval <= 0.05 ~ "*",
                           pval > 0.001 & pval <= 0.01 ~ "**",
                           pval <= 0.001 ~ "***")
  
  # Plot
  p = Seurat::VlnPlot(subsobj, group.by = "sample_type", pt.size = 0.3,
                      features = feature) +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Significance bar
    ggplot2::geom_segment(data = data.frame(1), inherit.aes = FALSE,
                          size = 0.5,
                          x = 1, xend = 2,
                          y = max(feature_expr)+0.3, yend = max(feature_expr)+0.3) +
    ggplot2::geom_label(data = data.frame(1), inherit.aes = FALSE,
                        x = 1.5, y = max(feature_expr)+0.35,
                        label = significance,
                        fill = NA, label.size = 0) +
    ggplot2::lims(y = c(0, max(feature_expr)+0.4)) +
    # Theme
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "none")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $DUSP1

## 
## $KLF6

## 
## $CLDN1

## 
## $CTGF

## 
## $S100A9

## 
## $CCL2

## 
## $IFITM3

## 
## $IFI27

Split by sample :

Seurat::VlnPlot(subsobj, group.by = "sample_identifier",
                features = "IFI27", cols = sample_info$color) +
  ggplot2::theme(axis.title.x = element_blank(),
                 legend.position = "none")

Heatmap for cluster 5 vs other ORS :

subsobj = subset(sobj_iblors, cluster_type == "ORS")

features_oi = c("YBX3", "TXNIP", "KRT14", "KRT15", "NEAT1",
                "FXYD3", "MT2A", "MT1E", "MT1X", "AQP3", "GLUL",
                # "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
                "FOS", "JUNB", "DUSP1", "ZFP36", "NFKBIZ",
                "ATF3", "RHOB",  "ETS2", "IL18", "KLF4", "KLF6", "KLF9",
                "KLF3", "KLF5", "COL17A1", "THSD4", "WNT3", "WNT4", "SLPI", "PLAT",
                "LAMB4", "DCN", "SPINK5",
                "GSTM3", "ALDH3A1",  "LGALS7B", "SLC38A2", "EHF",  "CLEC2B",
                "IL20RB", "IL1R2", "IFI27", "CXCL14", "HLA-C", "GPSM2", "DAAM1",   "ID1",
                "RNASET2", "HOPX", "POU3F1", "SPRY1", "AR", "PDGFC",
                "WFDC2", "WFDC5", "TSC22D3", "FGFR3",  "LY6D", "IGFBP3", 
                # Other ORS
                "APOE", "CTSB", "CALD1", "SOX4",
                "STMN1", "LMO4", "CEBPB", "TMEM45A", "GPX2", "C1QTNF12", "GJB6",
                "KRT6A", "KRT17", "RBP1", "CALML3", "PTN", "DAPK2",
                "EGLN3", "FILIP1L", "ADGRL3", "FST", "EFNB2", "SEMA5A",
                "FGFR1", "EGR2", "CLDN1", "DEFB1", "CARD18", "MGST1")

# Matrix
mat_expr = Seurat::GetAssayData(subsobj)
mat_expr = mat_expr[features_oi, ]
mat_expr = Matrix::t(mat_expr)
mat_expr = dynutils::scale_quantile(mat_expr) # between 0 and 1
mat_expr = Matrix::t(mat_expr)
dim(mat_expr) # genes x cells
## [1]   89 2022
## Colors
list_colors = list()
list_colors[["expression"]] = rev(RColorBrewer::brewer.pal(name = "RdBu", n = 9))
list_colors[["sample_type"]] = sample_type_colors
list_colors[["sample_identifier"]] = setNames(nm = sample_info$sample_identifier,
                                              sample_info$color)
list_colors[["population"]] = setNames(nm = c("IL1R2+ ORS", "other ORS"),
                                       c("black", color_markers["ORS"]))
list_colors[["nFeature_RNA"]] = circlize::colorRamp2(breaks = seq(from = min(subsobj$nFeature_RNA),
                                                                  to = max(subsobj$nFeature_RNA),
                                                                  length.out = 9),
                                                     colors = RColorBrewer::brewer.pal(name = "Greys", n = 9))

# Cells order
column_order = subsobj@meta.data %>%
  dplyr::mutate(seurat_clusters = factor(seurat_clusters, levels = c(5, 3, 0, 1, 7))) %>%
  dplyr::arrange(sample_type, seurat_clusters, sample_identifier) %>%
  rownames()
column_order = match(column_order, rownames(subsobj@meta.data))

# Annotation
ha_top = HeatmapAnnotation(sample_type = subsobj$sample_type,
                           sample_identifier = subsobj$sample_identifier,
                           population = ifelse(subsobj$cluster_type_sep5 == "ORS",
                                               yes = "other ORS", no = "IL1R2+ ORS"),
                           col = list(sample_type = list_colors[["sample_type"]],
                                      sample_identifier = list_colors[["sample_identifier"]],
                                      population = list_colors[["population"]]))

ha_bottom = HeatmapAnnotation(nFeature_RNA = subsobj$nFeature_RNA,
                              col = list(nFeature_RNA = list_colors[["nFeature_RNA"]]))

# Heatmap
ht = Heatmap(as.matrix(mat_expr),
             heatmap_legend_param = list(title = "Expression", at = c(0, 1), 
                                         labels = c("low", "high")),
             col = list_colors[["expression"]],
             top_annotation = ha_top,
             bottom_annotation = ha_bottom,
             # Cell grouping
             column_split = subsobj$sample_type %>% as.character(),
             cluster_columns = FALSE,
             column_order = column_order,
             column_title = NULL,
             show_column_dend = FALSE,
             show_column_names = FALSE,
             # Genes
             cluster_rows = FALSE,
             row_names_gp = grid::gpar(fontsize = 14, fontface = "plain"),
             # Style
             use_raster = FALSE,
             show_heatmap_legend = TRUE,
             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "right",
                     annotation_legend_side = "right")

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200", "IL18")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

names(plot_list) = genes
plot_list
## $KRT16

## 
## $COL17A1

## 
## $DST

## 
## $KRT6B

## 
## $IL1R2

## 
## $WNT3

## 
## $IFI27

## 
## $CXCL14

## 
## $IGFBP3

## 
## $KRT15

## 
## $CD200

## 
## $IL18

HFSCs to IBL and ORS

Settings

We load the merged dataset :

sobj_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_sobj_traj_tinga.rds"))
sobj_traj
## An object of class Seurat 
## 17050 features across 4986 samples within 1 assay 
## Active assay: RNA (17050 features, 2000 variable features)
##  8 dimensional reductions calculated: RNA_pca, RNA_pca_18_tsne, RNA_pca_18_umap, harmony, harmony_18_umap, harmony_18_tsne, harmony_dm, harmony_dm_5_umap

This is the projection name to visualize cells :

name2D = "harmony_dm"

We load the trajectory object for visualisation purpose :

my_traj = readRDS(paste0(data_dir, "/4_zoom/4_zoom_hfsc_iblmors/hfsc_iblmors_my_traj_tinga.rds"))
class(my_traj)
## [1] "dynwrap::with_dimred"     "dynwrap::with_trajectory"
## [3] "dynwrap::data_wrapper"    "list"

Preparation

We defined cell type based on individual object :

sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj_traj$cluster_type = dplyr::left_join(sobj_traj@meta.data[, c("cell_bc", "percent.mt")],
                                          sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                          by = "cell_bc")[, "cluster_type"] %>% as.character()
sobj_traj$cluster_type = ifelse(colnames(sobj_traj) %in% colnames(sobj_hfsc),
                                yes = "HFSC",
                                no = sobj_traj$cluster_type) %>%
  as.factor()

Figures

Cells on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_traj$cell_bc = colnames(sobj_traj)
sobj$is_traj = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                sobj_traj@meta.data[, c("cell_bc", "cluster_type")],
                                by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_traj", order = levels(sobj_traj$cluster_type)) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS", "HFSC")], bg_color),
                              breaks = c("IBL", "ORS", "HFSC", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() + Seurat::NoLegend()

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj_traj), replace = FALSE, size = ncol(sobj_traj))

# Extract coordinates
cells_coord = sobj_traj@reductions[[name2D]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$sample_type = sobj_traj$sample_type
cells_coord = cells_coord[order(sobj_traj$sample_type), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = sample_type)) +
  ggplot2::geom_point(size = 1.2) +
  ggplot2::scale_color_manual(values = sample_type_colors,
                              breaks = names(sample_type_colors)) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cluster type :

Seurat::DimPlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Pseudotime :

Seurat::FeaturePlot(sobj_traj, reduction = name2D, pt.size = 0.5,
                    features = "pseudotime") +
  ggplot2::scale_color_gradientn(colors = viridis::viridis(n = 100)) +
  ggplot2::lims(x = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 1]),
                y = range(sobj_traj@reductions[[name2D]]@cell.embeddings[, 2])) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes()

Pseudotime with dynplot’s function :

dynplot::plot_dimred(trajectory = my_traj,
                     dimred = sobj_traj[[name2D]]@cell.embeddings,
                     # Cells
                     color_cells = 'pseudotime',
                     size_cells = 1.6,
                     border_radius_percentage = 0,
                     # Trajectory
                     plot_trajectory = TRUE,
                     color_trajectory = "none",
                     label_milestones = FALSE,
                     size_milestones = 0,
                     size_transitions = 1)

Matrix cells

Settings

We load the immune cells dataset :

sobj_mat = readRDS(paste0(data_dir, "/4_zoom/5_zoom_matrix/matrix_sobj.rds"))
sobj_mat
## An object of class Seurat 
## 15937 features across 1529 samples within 1 assay 
## Active assay: RNA (15937 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_19_tsne, RNA_pca_19_umap, harmony, harmony_19_umap, harmony_19_tsne

This is the projection name to visualize cells :

name2D = "harmony_19_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/4_zoom/5_zoom_matrix/matrix_list_results.rds"))

lapply(list_results, FUN = names)
## $IRS
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $medulla
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $cortex
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"      
## 
## $cuticle
## [1] "mark"       "enrichr_hs" "enrichr_hd" "gsea"

Preparation

We defined factor levels for cluster type :

sobj_mat$cluster_type = factor(sobj_mat$cluster_type,
                               levels = c("medulla", "cortex", "IRS", "cuticle"))

table(sobj_mat$cluster_type, sobj_mat$sample_identifier)
##          
##           HS_1 HS_2 HS_3 HS_4 HS_5 HD_1 HD_2
##   medulla    6    7   24  117   36   41  101
##   cortex     6    1   45   41  115   32   73
##   IRS        1    1   12   20   47   15   52
##   cuticle   14   11   86  166  197   55  207

Figures

Matrix cells on the full atlas :

sobj$is_matrix = (colnames(sobj) %in% colnames(sobj_mat))

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_matrix", order = "TRUE") +
  ggplot2::scale_color_manual(values = c(category_color[["matrix"]], bg_color),
                              breaks = c(TRUE, FALSE)) +
  ggplot2::labs(title = "Matrix cells",
                subtitle = paste0(ncol(sobj_mat), " cells")) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_text(hjust = 0.5, face = "bold"),
                 plot.subtitle = element_text(hjust = 0.5)) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_mat, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Clustering :

Seurat::DimPlot(sobj_mat, reduction = name2D, pt.size = 1,
                group.by = "seurat_clusters", label = TRUE) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Barplot with proportion :

quantif_total = table(sobj$sample_identifier) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "nb_cells"))

# Number of matrix cells
quantif_immune = table(sobj_mat$sample_identifier, sobj_mat$cluster_type) %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Sample", "CellType", "Number")) %>%
  # proportion for the whole dataset
  dplyr::left_join(., quantif_total, by = "Sample") %>%
  dplyr::mutate(prop_cells = 100*round(Number/nb_cells,4)) %>%
  `colnames<-`(c("Sample", "CellType", "Number", "Total", "Proportion"))

ggplot2::ggplot(data = quantif_immune,
                aes(x = Sample, y = Proportion, fill = CellType)) +
  ggplot2::geom_bar(stat = "identity", position = position_dodge(width = 0.7),
                    color = "black", size = 0.25, width = 0.7) +
  ggplot2::scale_fill_manual(values = unlist(color_markers[levels(sobj_mat$cluster_type)]),
                             breaks = names(color_markers[levels(sobj_mat$cluster_type)]),
                             name = "Cell Type") +
  # Theme
  ggplot2::scale_y_continuous(limits = c(0, max(quantif_immune$Proportion)),
                              expand = ggplot2::expansion(add = c(0, 1))) +
  ggplot2::labs(y = "% of matrix cells by sample") +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.title.x = element_blank(),
                 axis.line.x = element_line(colour = "lightgray"),
                 text = element_text(size = 15),
                 axis.text.x = element_text(angle = 0, hjust = 0.5),
                 panel.grid.major.y = element_line(color = "lightgray", size = 0.5),
                 panel.grid.minor.y = element_line(color = "lightgray", size = 0.5))

GSEA plots :

the_gs_name = "GOCC_KERATIN_FILAMENT" 
the_content = list_results$cortex$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$cortex$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "GOBP_INTERMEDIATE_FILAMENT_ORGANIZATION" 
the_content = list_results$cortex$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$cortex$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Violin plot

features_oi = c("KRT32")

plot_list = lapply(features_oi, FUN = function(feature) {
  feature_expr = Seurat::FetchData(sobj_mat, feature)[, 1]
  y_max = max(feature_expr)*1.1
  
  # Dataframe stat
  pop_oi = levels(sobj_mat$cluster_type)
  
  df_stat = lapply(c(1:length(pop_oi)), FUN = function(i) {
    pop = pop_oi[i]
    
    # Stat
    feature_hs = feature_expr[sobj_mat$sample_type == "HS" & sobj_mat$cluster_type == pop]
    feature_hd = feature_expr[sobj_mat$sample_type == "HD" & sobj_mat$cluster_type == pop]
    feature_hs_vs_hd = stats::t.test(feature_hs, feature_hd)
    significance = significant(feature_hs_vs_hd$p.value)
    
    # Output (x, pop, signif)
    row_i = c(i, pop, significance)
    
    return(row_i)
  }) %>% do.call(rbind.data.frame, .) %>%
    `colnames<-`(c("x", "pop", "signif")) %>%
    dplyr::mutate(x = as.numeric(x))
  
  # Plot
  p = Seurat::VlnPlot(sobj_mat, features = feature,
                      group.by = "cluster_type",
                      split.by = "sample_type") +
    ggplot2::scale_fill_manual(values = sample_type_colors,
                               breaks = names(sample_type_colors)) +
    # Stat
    ggplot2::geom_segment(data = df_stat, inherit.aes = FALSE, size = 0.5,
                          mapping = aes(x = x - 0.25, xend = x + 0.25),
                          y = y_max, yend = y_max) +
    ggplot2::geom_label(data = df_stat, inherit.aes = FALSE,
                        mapping = aes(x = pop, label = signif),
                        y = y_max*1.05,
                        fill = NA, label.size = 0) +
    # Theme
    ggplot2::lims(y = c(0, y_max*1.1)) +
    ggplot2::theme(axis.title.x = element_blank(),
                   legend.position = "right")
  
  return(p)
})

names(plot_list) = features_oi
plot_list
## $KRT32

OEP002321 dataset

Settings

We load the dataset containing all cells :

sobj = readRDS(paste0(data_dir, "/5_wu/3_combined/wu_sobj.rds"))
sobj
## An object of class Seurat 
## 17727 features across 17762 samples within 1 assay 
## Active assay: RNA (17727 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_38_tsne, RNA_pca_38_umap, harmony, harmony_38_umap, harmony_38_tsne

This is the projection name to visualize cells :

name2D = "harmony_38_tsne"
name2D_atlas = name2D

These are all the samples analyzed :

sample_info = readRDS(paste0(data_dir, "/5_wu/1_metadata/wu_sample_info.rds"))

# Nb cells by dataset
to_plot = table(sobj$sample_identifier) %>%
  as.data.frame.table(., stringsAsFactors = FALSE) %>%
  `colnames<-`(c("sample_identifier", "nb_cells")) %>%
  dplyr::left_join(x = ., y = sample_info, by = "sample_identifier") 

# patchwork
plot_list = aquarius::fig_plot_gb(to_plot, title = "Available datasets")
patchwork::wrap_plots(plot_list) +
  patchwork::plot_layout(design = "A\nB", heights = c(0.1,5)) &
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 15))

We define cluster type and cluster category :

sobj$cell_type = sobj$cell_type %>%
  as.character() %>%
  factor(., levels = names(color_markers))

cluster_type = table(sobj$cell_type, sobj$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj$cell_type)[cluster_type])

sobj$cluster_type = cluster_type[sobj$seurat_clusters]
sobj$cluster_type = factor(sobj$cluster_type,
                           levels = levels(sobj$cell_type)) %>%
  base::droplevels()
sobj$cluster_category = custom_order_cell_type[sobj$cluster_type, "cell_category"]
sobj$cluster_category = factor(sobj$cluster_category,
                               levels = names(category_color))

Global figures

Project name :

# Random order
set.seed(1234)
rnd_order = sample(colnames(sobj), replace = FALSE, size = ncol(sobj))

# Extract coordinates
cells_coord = sobj@reductions[[name2D_atlas]]@cell.embeddings %>%
  as.data.frame() %>%
  `colnames<-`(c("Dim1", "Dim2"))
cells_coord$project_name = sobj$project_name
cells_coord = cells_coord[(rnd_order), ]

# Plot
ggplot2::ggplot(cells_coord, aes(x = Dim1, y = Dim2, col = project_name)) +
  ggplot2::geom_point(size = 0.5) +
  ggplot2::scale_color_manual(values = sample_info$color,
                              breaks = sample_info$project_name) +
  ggplot2::theme_void() +
  ggplot2::theme(aspect.ratio = 1,
                 legend.position = "none")

Cell type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cell_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Cluster type annotation :

Seurat::DimPlot(sobj, reduction = name2D, pt.size = 0.5,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

Gene expression to assess annotation :

genes = c("PTPRC", "MSX2", "KRT14")
names(genes) = c("immune cells", "matrix cells", "non-matrix cells")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  pop = names(genes)[gene_id]
  
  sobj$my_gene = Seurat::FetchData(sobj, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj, features = "my_gene", reduction = name2D_atlas) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) + 
    # subtitle = pop) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

Dotplot :

custom_order_cell_type = custom_order_cell_type[levels(sobj$cluster_type), c("cell_type", "cell_category")]

plot_list = Seurat::DotPlot(sobj,
                            features = c("PTPRC",
                                         "CD3E", "CD4",
                                         "CD207", "AIF1",
                                         # "PRDM1", "KRT85",
                                         "MSX2",
                                         "KRT32", "KRT35",
                                         "KRT31", "PRR9",
                                         "BAMBI", "ALDH1A3",
                                         "KRT71", "KRT73",
                                         "TOP2A", "MCM5",
                                         "KRT14", "CXCL14",
                                         "KRT15", "COL17A1",
                                         "DIO2", "TCEAL2",
                                         "KRT16", "KRT6C",
                                         "SPINK5", "LY6D"),
                            group.by = "cluster_type", scale = TRUE,
                            scale.by = "radius", scale.min = NA, scale.max = NA) +
  ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
  ggplot2::theme(legend.position = "bottom",
                 legend.direction = "vertical",
                 # legend.justification = "bottom",
                 legend.box = "horizontal",
                 legend.box.margin = margin(0,25,0,0),
                 axis.title = element_blank(),
                 axis.ticks.y = element_blank(),
                 axis.text.y = element_blank(),
                 axis.line.y = element_blank(),
                 axis.text.x = element_text(angle = 45, hjust = 1, size = 11, color = "black"),
                 plot.margin = unit(c(0,0.5,0,0), "cm"))

p = ggplot2::ggplot(custom_order_cell_type, aes(y = cell_type, x = 0)) +
  ggplot2::geom_point(size = 0) +
  ggplot2::geom_segment(aes(y = 0.5, yend = 2.5, x = 0, xend = 0), size = 6, col = category_color["immune cells"]) +
  ggplot2::geom_segment(aes(y = 2.5, yend = 7.5, x = 0, xend = 0), size = 6, col = category_color["matrix"]) +
  ggplot2::geom_segment(aes(y = 7.5, yend = 11.5, x = 0, xend = 0), size = 6, col = category_color["non matrix"]) +
  ggplot2::scale_x_continuous(expand = c(0,0), limits = c(0,0)) +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = element_blank(),
                 axis.ticks.x = element_blank(),
                 axis.title = element_blank(),
                 axis.line.x = element_blank(),
                 axis.text.y = element_text(size = 12, color = "black"),
                 plot.margin = unit(c(0.5,0,0,0.5), "cm"))

plot_list = patchwork::wrap_plots(p, plot_list,
                                  nrow = 1, widths = c(1, 25))
plot_list

IBL and ORS dataset

We load the IBL + ORS dataset :

sobj_iblors = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_sobj.rds"))
sobj_iblors
## An object of class Seurat 
## 15541 features across 8026 samples within 1 assay 
## Active assay: RNA (15541 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_20_tsne, RNA_pca_20_umap, harmony, harmony_20_umap, harmony_20_tsne

This is the projection name to visualize cells :

name2D = "harmony_20_tsne"

To represent results from differential expression, we load the analyses results :

list_results = readRDS(paste0(data_dir, "/5_wu/4_ibl_ors/iblmors_list_results.rds"))

lapply(list_results, FUN = names)
## $IBL_vs_ORS
## [1] "mark"        "enrichr_ibl" "enrichr_ors" "gsea"

We defined cluster type :

cluster_type = table(sobj_iblors$cell_type, sobj_iblors$seurat_clusters) %>%
  prop.table(., margin = 2) %>%
  apply(., 2, which.max)
cluster_type = setNames(nm = names(cluster_type),
                        levels(sobj_iblors$cell_type)[cluster_type])

sobj_iblors$cluster_type = cluster_type[sobj_iblors$seurat_clusters]
sobj_iblors$cluster_type = factor(sobj_iblors$cluster_type,
                                  levels = c("IBL", "ORS"))

IBL + ORS figure

IBL + ORS on the full atlas :

sobj$cell_bc = colnames(sobj)
sobj_iblors$cell_bc = colnames(sobj_iblors)
sobj$is_iblors = dplyr::left_join(sobj@meta.data[, c("cell_bc", "percent.mt")],
                                  sobj_iblors@meta.data[, c("cell_bc", "cluster_type")],
                                  by = "cell_bc")[, "cluster_type"]

Seurat::DimPlot(sobj, reduction = name2D_atlas, pt.size = 0.000001,
                group.by = "is_iblors", order = FALSE) +
  ggplot2::scale_color_manual(values = c(color_markers[c("IBL", "ORS")], bg_color),
                              breaks = c("IBL", "ORS", NA), na.value = bg_color) +
  ggplot2::theme(aspect.ratio = 1,
                 plot.title = element_blank()) +
  Seurat::NoAxes() + Seurat::NoLegend()

Cluster type :

Seurat::DimPlot(sobj_iblors, reduction = name2D, pt.size = 1,
                group.by = "cluster_type", cols = color_markers) +
  ggplot2::theme(aspect.ratio = 1) +
  Seurat::NoAxes() +
  Seurat::NoLegend()

DE genes between IBL and ORS :

mark = list_results$IBL_vs_ORS$mark
mark$gene_name = rownames(mark)
mark_label = rbind(
  # up-regulated in IBL
  mark %>% dplyr::top_n(., n = 20, wt = avg_logFC),
  # up-regulated in ORS
  mark %>% dplyr::top_n(., n = 20, wt = -avg_logFC),
  # representative and selective for IBL
  mark %>% dplyr::top_n(., n = 20, wt = (pct.1 - pct.2)),
  # representative and selective for ORS
  mark %>% dplyr::top_n(., n = 20, wt = -(pct.1 - pct.2))) %>%
  dplyr::distinct()
mark_label = mark_label[!grepl(rownames(mark_label), pattern = "^MT"), ]

avg_logFC_range = setNames(c(min(mark_label$avg_logFC), -1, 0, 1, max(mark_label$avg_logFC)),
                           nm = c("dodgerblue4", "dodgerblue3", "#B7B7B7", "firebrick3", "firebrick4"))


ggplot2::ggplot(mark, aes(x = pct.1, y = pct.2, col = avg_logFC)) +
  ggplot2::geom_abline(slope = 1, intercept = 0, lty = 2) +
  ggplot2::geom_point() +
  ggrepel::geom_label_repel(data = mark_label, max.overlaps = Inf,
                            aes(x = pct.1, y = pct.2, label = gene_name),
                            col = "black", fill = NA, size = 3.5, label.size = NA) +
  ggplot2::labs(x = "Enriched in IBL",
                y = "Enriched in ORS") +
  ggplot2::scale_color_gradientn(colors = names(avg_logFC_range),
                                 values = scales::rescale(unname(avg_logFC_range))) +
  ggplot2::theme_classic() +
  ggplot2::theme(aspect.ratio = 1)

GSEA plot :

the_gs_name = "REACTOME_KERATINIZATION" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

the_gs_name = "HALLMARK_INTERFERON_GAMMA_RESPONSE" 
the_content = list_results$IBL_vs_ORS$gsea@result %>%
  dplyr::filter(ID == the_gs_name)
the_subtitle = paste0("\nNES : ", round(the_content$NES, 2),
                      "\t|\tpvalue : ", round(the_content$pvalue, 4),
                      "\t|\tset size : ", the_content$setSize, " genes")

enrichplot::gseaplot2(x = list_results$IBL_vs_ORS$gsea,
                      geneSetID = the_gs_name) +
  ggplot2::labs(title = the_gs_name,
                subtitle = the_subtitle) +
  ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                           margin = ggplot2::margin(3, 3, 5, 3)),
                 plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                          size = 10))

Genes of interest :

genes = c("KRT16", "COL17A1", "DST", "KRT6B", "IL1R2", "WNT3",
          "IFI27", "CXCL14", "IGFBP3", "KRT15", "CD200")

plot_list = lapply(c(1:length(genes)), FUN = function(gene_id) {
  gene = genes[[gene_id]]
  
  sobj_iblors$my_gene = Seurat::FetchData(sobj_iblors, gene)[, 1] %>%
    aquarius::run_rescale(., new_min = 0, new_max = 10)
  
  Seurat::FeaturePlot(sobj_iblors, features = "my_gene", reduction = name2D, pt.size = 0.25) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene,
                                   breaks = seq(0, 10, by = 2.5),
                                   labels = c("min", rep("", 3), "max")) +
    ggplot2::labs(title = gene) +
    ggplot2::theme(aspect.ratio = 1,
                   plot.title = element_text(hjust = 0.5, size = 17),
                   plot.subtitle = element_text(hjust = 0.5, size = 15),
                   legend.text = element_text(size = 15),
                   legend.position = "none") +
    Seurat::NoAxes()
})

plot_list
## [[1]]

## 
## [[2]]

## 
## [[3]]

## 
## [[4]]

## 
## [[5]]

## 
## [[6]]

## 
## [[7]]

## 
## [[8]]

## 
## [[9]]

## 
## [[10]]

## 
## [[11]]

Supplementary tables

In this section, we save files to associate with the manuscript, as supplementary tables.

Table S2 (package version)

We load the table :

package_version = read.table(paste0(".", "/data/info_to_install_2023_04_17.txt"),
                             header = TRUE)
head(package_version)
##   order package_name  version
## 1     1      acepack    1.4.1
## 2     2    ADGofTest      0.3
## 3     3    backports    1.2.1
## 4     4    base64enc    0.1-3
## 5     5           BH 1.72.0-3
## 6     6          bit    4.0.4
##                                                                              url
## 1                    https://cran.r-project.org/src/contrib/acepack_1.4.1.tar.gz
## 2                    https://cran.r-project.org/src/contrib/ADGofTest_0.3.tar.gz
## 3 http://cran.r-project.org/src/contrib/Archive/backports/backports_1.2.1.tar.gz
## 4                  https://cran.r-project.org/src/contrib/base64enc_0.1-3.tar.gz
## 5            http://cran.r-project.org/src/contrib/Archive/BH/BH_1.72.0-3.tar.gz
## 6             http://cran.r-project.org/src/contrib/Archive/bit/bit_4.0.4.tar.gz
##           type
## 1    CRAN_last
## 2    CRAN_last
## 3 CRAN_archive
## 4    CRAN_last
## 5 CRAN_archive
## 6 CRAN_archive

Columns correspond to :

  • order : order to install package such that one never need to install dependencies
  • package_name : package name
  • version : installed version, on the local machine
  • url : the package was installed in the Singularity container using this url

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(package_version[, c(1:4)],
                     file = paste0(".", "/data/Supplementary Table 2.xlsx"))

Table S3 (cell type annotation)

We load the table :

cell_markers = readRDS(paste0(data_dir, "/1_metadata/hs_hd_cell_markers.rds"))
lengths(cell_markers)
##      CD4 T cells      CD8 T cells Langerhans cells      macrophages 
##               13               13                9               10 
##          B cells          cuticle           cortex          medulla 
##               16               15               16               10 
##              IRS    proliferative              IBL              ORS 
##               16               20               15               16 
##              IFE             HFSC      melanocytes        sebocytes 
##               17               17               10                8

We save this table, except the last column (just for me) :

openxlsx::write.xlsx(cell_markers,
                     file = paste0(".", "/data/Supplementary Table 3.xlsx"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
##  [1] parallel  stats4    grid      stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] org.Mm.eg.db_3.10.0   AnnotationDbi_1.48.0  IRanges_2.20.2       
##  [4] S4Vectors_0.24.4      Biobase_2.46.0        BiocGenerics_0.32.0  
##  [7] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [10] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 dyndimred_1.0.3            
##   [7] vctrs_0.3.8                 usethis_2.0.1              
##   [9] dynwrap_1.2.1               blob_1.2.1                 
##  [11] survival_3.2-13             prodlim_2019.11.13         
##  [13] dynutils_1.0.5              later_1.3.0                
##  [15] DBI_1.1.1                   R.utils_2.11.0             
##  [17] SingleCellExperiment_1.8.0  rappdirs_0.3.3             
##  [19] uwot_0.1.8                  dqrng_0.2.1                
##  [21] jpeg_0.1-8.1                zlibbioc_1.32.0            
##  [23] pspline_1.0-18              pcaMethods_1.78.0          
##  [25] mvtnorm_1.1-1               htmlwidgets_1.5.4          
##  [27] GlobalOptions_0.1.2         future_1.22.1              
##  [29] UpSetR_1.4.0                laeken_0.5.2               
##  [31] leiden_0.3.3                clustree_0.4.3             
##  [33] lmds_0.1.0                  scater_1.14.6              
##  [35] irlba_2.3.3                 markdown_1.1               
##  [37] DEoptimR_1.0-9              tidygraph_1.1.2            
##  [39] Rcpp_1.0.9                  readr_2.0.2                
##  [41] KernSmooth_2.23-17          carrier_0.1.0              
##  [43] promises_1.1.0              gdata_2.18.0               
##  [45] DelayedArray_0.12.3         limma_3.42.2               
##  [47] graph_1.64.0                RcppParallel_5.1.4         
##  [49] Hmisc_4.4-0                 fs_1.5.2                   
##  [51] RSpectra_0.16-0             fastmatch_1.1-0            
##  [53] ranger_0.12.1               digest_0.6.25              
##  [55] png_0.1-7                   sctransform_0.2.1          
##  [57] cowplot_1.0.0               DOSE_3.12.0                
##  [59] here_1.0.1                  TInGa_0.0.0.9000           
##  [61] dynplot_1.1.0               ggraph_2.0.3               
##  [63] pkgconfig_2.0.3             GO.db_3.10.0               
##  [65] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [67] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [69] DropletUtils_1.6.1          reticulate_1.26            
##  [71] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [73] circlize_0.4.15             beeswarm_0.4.0             
##  [75] GetoptLong_1.0.5            xfun_0.35                  
##  [77] bslib_0.3.1                 zoo_1.8-10                 
##  [79] tidyselect_1.1.0            GA_3.2                     
##  [81] reshape2_1.4.4              purrr_0.3.4                
##  [83] ica_1.0-2                   pcaPP_1.9-73               
##  [85] viridisLite_0.3.0           rtracklayer_1.46.0         
##  [87] rlang_1.0.2                 hexbin_1.28.1              
##  [89] jquerylib_0.1.4             dyneval_0.9.9              
##  [91] glue_1.4.2                  waldo_0.3.1                
##  [93] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [95] stringr_1.4.0               lava_1.6.7                 
##  [97] europepmc_0.3               DESeq2_1.26.0              
##  [99] recipes_0.1.17              labeling_0.3               
## [101] httpuv_1.5.2                class_7.3-17               
## [103] BiocNeighbors_1.4.2         DO.db_2.9                  
## [105] annotate_1.64.0             jsonlite_1.7.2             
## [107] XVector_0.26.0              bit_4.0.4                  
## [109] mime_0.9                    aquarius_0.1.5             
## [111] Rsamtools_2.2.3             gridExtra_2.3              
## [113] gplots_3.0.3                stringi_1.4.6              
## [115] processx_3.5.2              gsl_2.1-6                  
## [117] bitops_1.0-6                cli_3.0.1                  
## [119] batchelor_1.2.4             RSQLite_2.2.0              
## [121] randomForest_4.6-14         tidyr_1.1.4                
## [123] data.table_1.14.2           rstudioapi_0.13            
## [125] units_0.7-2                 GenomicAlignments_1.22.1   
## [127] nlme_3.1-147                qvalue_2.18.0              
## [129] scran_1.14.6                locfit_1.5-9.4             
## [131] scDblFinder_1.1.8           listenv_0.8.0              
## [133] ggthemes_4.2.4              gridGraphics_0.5-0         
## [135] R.oo_1.24.0                 dbplyr_1.4.4               
## [137] TTR_0.24.2                  readxl_1.3.1               
## [139] lifecycle_1.0.1             timeDate_3043.102          
## [141] ggpattern_0.3.1             munsell_0.5.0              
## [143] cellranger_1.1.0            R.methodsS3_1.8.1          
## [145] proxyC_0.1.5                visNetwork_2.0.9           
## [147] caTools_1.18.0              codetools_0.2-16           
## [149] GenomeInfoDb_1.22.1         vipor_0.4.5                
## [151] lmtest_0.9-38               msigdbr_7.5.1              
## [153] htmlTable_1.13.3            triebeard_0.3.0            
## [155] lsei_1.2-0                  xtable_1.8-4               
## [157] ROCR_1.0-7                  classInt_0.4-3             
## [159] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [161] abind_1.4-5                 farver_2.0.3               
## [163] parallelly_1.28.1           RANN_2.6.1                 
## [165] askpass_1.1                 GenomicRanges_1.38.0       
## [167] RcppAnnoy_0.0.16            tibble_3.1.5               
## [169] ggdendro_0.1-20             cluster_2.1.0              
## [171] future.apply_1.5.0          Seurat_3.1.5               
## [173] dendextend_1.15.1           Matrix_1.3-2               
## [175] ellipsis_0.3.2              prettyunits_1.1.1          
## [177] lubridate_1.7.9             ggridges_0.5.2             
## [179] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [181] fgsea_1.12.0                remotes_2.4.2              
## [183] scBFA_1.0.0                 destiny_3.0.1              
## [185] VIM_6.1.1                   testthat_3.1.0             
## [187] htmltools_0.5.2             BiocFileCache_1.10.2       
## [189] yaml_2.2.1                  utf8_1.1.4                 
## [191] plotly_4.9.2.1              XML_3.99-0.3               
## [193] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [195] foreign_0.8-76              withr_2.5.0                
## [197] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [199] xgboost_1.4.1.1             bit64_4.0.5                
## [201] foreach_1.5.0               robustbase_0.93-9          
## [203] Biostrings_2.54.0           GOSemSim_2.13.1            
## [205] rsvd_1.0.3                  memoise_2.0.0              
## [207] evaluate_0.18               forcats_0.5.0              
## [209] rio_0.5.16                  geneplotter_1.64.0         
## [211] tzdb_0.1.2                  caret_6.0-86               
## [213] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [215] curl_4.3                    fdrtool_1.2.15             
## [217] fansi_0.4.1                 highr_0.8                  
## [219] urltools_1.7.3              xts_0.12.1                 
## [221] GSEABase_1.48.0             acepack_1.4.1              
## [223] edgeR_3.28.1                checkmate_2.0.0            
## [225] scds_1.2.0                  cachem_1.0.6               
## [227] npsurv_0.4-0                babelgene_22.3             
## [229] rjson_0.2.20                openxlsx_4.1.5             
## [231] ggrepel_0.9.1               clue_0.3-60                
## [233] rprojroot_2.0.2             stabledist_0.7-1           
## [235] tools_3.6.3                 sass_0.4.0                 
## [237] nichenetr_1.1.1             magrittr_2.0.1             
## [239] RCurl_1.98-1.2              proxy_0.4-24               
## [241] car_3.0-11                  ape_5.3                    
## [243] ggplotify_0.0.5             xml2_1.3.2                 
## [245] httr_1.4.2                  assertthat_0.2.1           
## [247] rmarkdown_2.18              boot_1.3-25                
## [249] globals_0.14.0              R6_2.4.1                   
## [251] Rhdf5lib_1.8.0              nnet_7.3-14                
## [253] RcppHNSW_0.2.0              progress_1.2.2             
## [255] genefilter_1.68.0           statmod_1.4.34             
## [257] gtools_3.8.2                shape_1.4.6                
## [259] sf_1.0-3                    HDF5Array_1.14.4           
## [261] BiocSingular_1.2.2          rhdf5_2.30.1               
## [263] splines_3.6.3               AUCell_1.8.0               
## [265] carData_3.0-4               colorspace_1.4-1           
## [267] generics_0.1.0              base64enc_0.1-3            
## [269] dynfeature_1.0.0            smoother_1.1               
## [271] gridtext_0.1.1              pillar_1.6.3               
## [273] tweenr_1.0.1                sp_1.4-1                   
## [275] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [277] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [279] gtable_0.3.0                zip_2.2.0                  
## [281] knitr_1.41                  latticeExtra_0.6-29        
## [283] biomaRt_2.42.1              fastmap_1.1.0              
## [285] ADGofTest_0.3               copula_1.0-0               
## [287] doParallel_1.0.15           vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] ipred_0.9-12                enrichplot_1.6.1           
## [295] hms_1.1.1                   ggforce_0.3.1              
## [297] Rtsne_0.15                  shiny_1.7.1                
## [299] gridpattern_0.3.1           numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJGaWd1cmVzIgphdXRob3I6ICJBdWRyZXkiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKLS0tCgo8c3R5bGU+CmJvZHkgewp0ZXh0LWFsaWduOiBqdXN0aWZ5fQo8L3N0eWxlPgoKPCEtLSBBdXRvbWF0aWNhbGx5IGNvbXB1dGVzIGFuZCBwcmludHMgaW4gdGhlIG91dHB1dCB0aGUgcnVubmluZyB0aW1lIGZvciBhbnkgY29kZSBjaHVuayAtLT4KYGBge3IsIGVjaG89RkFMU0V9CiMgaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcm1hcmtkb3duL2lzc3Vlcy8xNDUzCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKICAgIAogICAgaWYgKGlzRkFMU0Uob3B0aW9uc1tbcGFzdGUwKCJmb2xkXyIsIHR5cGUpXV0pKSByZXR1cm4ocmVzKQogICAgCiAgICBwYXN0ZTAoCiAgICAgICI8ZGV0YWlscz48c3VtbWFyeT4iLCAic2hvdyIsICI8L3N1bW1hcnk+XG5cbiIsCiAgICAgIHJlcywKICAgICAgIlxuXG48L2RldGFpbHM+IgogICAgKQogIH0KfQprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgb3V0cHV0ID0gaG9va19mb2xkYWJsZSgib3V0cHV0IiksCiAgcGxvdCA9IGhvb2tfZm9sZGFibGUoInBsb3QiKSwKICB0aW1lX2l0ID0gbG9jYWwoewogICAgbm93ID0gTlVMTAogICAgZnVuY3Rpb24oYmVmb3JlLCBvcHRpb25zKSB7CiAgICAgIGlmIChvcHRpb25zJHRpbWVfaXQpIHsKICAgICAgICBpZiAoYmVmb3JlKSB7CiAgICAgICAgICBub3cgPD0gU3lzLnRpbWUoKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXMgPSBkaWZmdGltZShTeXMudGltZSgpLCBub3csIHVuaXRzID0gInNlY3MiKQogICAgICAgICAgcGFzdGUoIihUaW1lIHRvIHJ1biA6Iiwgcm91bmQocmVzLCBkaWdpdHMgPSAyKSwgInMpIikKICAgICAgICB9CiAgICAgIH0KICAgIH0KICB9KQopCmBgYAoKPCEtLSBTZXQgZGVmYXVsdCBwYXJhbWV0ZXJzIGZvciBhbGwgY2h1bmtzIC0tPgpgYGB7ciwgc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc2V0LnNlZWQoMTMzN0wpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgIyBkaXNwbGF5IGNvZGUKICAgICAgICAgICAgICAgICAgICAgICMgZGlzcGxheSBjaHVuayBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfb3V0cHV0ID0gRkFMU0UsICMgdXNlZnVsIGZvciBzZXNzaW9uSW5mbygpCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX3Bsb3QgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBmaWd1cmUgc2V0dGluZ3MKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInLAogICAgICAgICAgICAgICAgICAgICAgZmlnLndpZHRoID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgc29tZXRoaW5nIGFib3V0IHNlZWQsIGNodW5rIGFuZCBSbWFya2Rvd24gY29tcGlsYXRpb24KICAgICAgICAgICAgICAgICAgICAgICMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzk0MTcwMDMvbG9uZy12ZWN0b3JzLW5vdC1zdXBwb3J0ZWQteWV0LWVycm9yLWluLXJtZC1idXQtbm90LWluLXItc2NyaXB0CiAgICAgICAgICAgICAgICAgICAgICAjIGNhY2hlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlLmxhenkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHJ1bnRpbWUgYWZ0ZXIgY2h1bmsKICAgICAgICAgICAgICAgICAgICAgIHRpbWVfaXQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzYXZlIGZpZ3VyZXMgaW4gUERGIGluIGEgc2VwYXJhdGUgZm9sZGVyCiAgICAgICAgICAgICAgICAgICAgICBkZXYgPSBjKCdwbmcnLCAncGRmJyksICMgdGlmZiBvciBwZGYgYWxvbmUgcmVuZGVycyBiYWQgaW4gaHRtbAogICAgICAgICAgICAgICAgICAgICAgIyBkcGkgPSAzMDAsCiAgICAgICAgICAgICAgICAgICAgICBmaWcucGF0aCA9ICJmaWd1cmVzX2RldGFpbC8iLAogICAgICAgICAgICAgICAgICAgICAgcGRmLm9wdGlvbnMoZW5jb2RpbmcgPSAiSVNPTGF0aW45LmVuYyIpKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBwcmVwYXJlIHRoZSBmaWd1cmVzIGZvciB0aGUgcGFwZXIuCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKCi5saWJQYXRocygpCmBgYAoKCiMgUHJlcGFyYXRpb24KCkhlcmUgYXJlIHRoZSBmb2xkZXJzIHdoZXJlIGFuYWx5emVzIGFyZSBzdG9yZWQgOgoKYGBge3IgbG9jYXRpb25zfQpkYXRhX2RpciA9ICIuLy4uIgpsaXN0LmZpbGVzKGRhdGFfZGlyKQpgYGAKCgpXZSBsb2FkIHRoZSBkYXRhc2V0IGNvbnRhaW5pbmcgYWxsIGNlbGxzIDoKCmBgYHtyIGxvYWRfYWxsX3NvYmp9CnNvYmogPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi8zX2NvbWJpbmVkL2hzX2hkX3NvYmoucmRzIikpCnNvYmoKYGBgCgpUaGVzZSBhcmUgYWxsIHRoZSBzYW1wbGVzIGFuYWx5emVkIDoKCmBgYHtyIHNhbXBsZV9pbmZvLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDN9CnNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9zYW1wbGVfaW5mby5yZHMiKSkKCiMgTmIgY2VsbHMgYnkgZGF0YXNldAp0b19wbG90ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSguLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHggPSAuLCB5ID0gc2FtcGxlX2luZm8sIGJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIikgCgojIHBhdGNod29yawpwbG90X2xpc3QgPSBhcXVhcml1czo6ZmlnX3Bsb3RfZ2IodG9fcGxvdCwgdGl0bGUgPSAiQXZhaWxhYmxlIGRhdGFzZXRzIikKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgKwogIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZGVzaWduID0gIkFcbkIiLCBoZWlnaHRzID0gYygwLjEsNSkpICYKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTUpKQpgYGAKClRoZXNlIGFyZSB0aGUgY3VzdG9tIGNvbG9ycyBmb3IgY2VsbCBwb3B1bGF0aW9ucyA6CgpgYGB7ciBjb2xvcl9tYXJrZXJzLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjb2xvcl9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tuYW1lcyhjb2xvcl9tYXJrZXJzKSAhPSAibWVsYW5vY3l0ZXMiXQpvcnNfY29sb3IgPSBjb2xvcl9tYXJrZXJzWyJPUlMiXQpjb2xvcl9tYXJrZXJzWyJPUlMiXSA9IGNvbG9yX21hcmtlcnNbIklGRSJdIApjb2xvcl9tYXJrZXJzWyJJRkUiXSA9IG9yc19jb2xvcgpjb2xvcl9tYXJrZXJzWyJCIGNlbGxzIl0gPSAiY2hvY29sYXRlMyIKcm0ob3JzX2NvbG9yKQoKIyByZS1vcmRlcgpjb2xvcl9tYXJrZXJzID0gY29sb3JfbWFya2Vyc1tjKCJDRDQgVCBjZWxscyIsICJDRDggVCBjZWxscyIsICJMYW5nZXJoYW5zIGNlbGxzIiwgIm1hY3JvcGhhZ2VzIiwgIkIgY2VsbHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXRpY2xlIiwgImNvcnRleCIsICJtZWR1bGxhIiwgIklSUyIsICJwcm9saWZlcmF0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSEZTQyIsICJPUlMiLCAiSUJMIiwgIklGRSIsICJzZWJvY3l0ZXMiKV0KCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoY29sb3JfbWFya2VycyksCiAgICAgICAgICAgY29sb3IgPSB1bmxpc3QoY29sb3JfbWFya2VycykpICU+JQogIGdncGxvdDI6OmdncGxvdCguLCBhZXMoeCA9IGNlbGxfdHlwZSwgeSA9IDAsIGZpbGwgPSBjZWxsX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChwY2ggPSAyMSwgc2l6ZSA9IDUpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2VycyksIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnMpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEsIGFuZ2xlID0gMjApKQpgYGAKCldlIGRlZmluZSBjdXN0b20gY29sb3JzIGZvciBzYW1wbGUgdHlwZSA6CgpgYGB7ciBzYW1wbGVfdHlwZV9jb2xvcnMsIGZpZy53aWR0aCA9IDMsIGZpZy5oZWlnaHQgPSAwLjc1LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0Kc2FtcGxlX3R5cGVfY29sb3JzID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc2FtcGxlX2luZm8kc2FtcGxlX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCIjQzU1RjQwIiwgIiMyQzc4RTYiKSkKCmRhdGEuZnJhbWUoY2VsbF90eXBlID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChzYW1wbGVfdHlwZV9jb2xvcnMpKSAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoLiwgYWVzKHggPSBjZWxsX3R5cGUsIHkgPSAwLCBmaWxsID0gY2VsbF90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQocGNoID0gMjEsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KHNhbXBsZV90eXBlX2NvbG9ycyksIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCldlIHNldCBhIGJhY2tncm91bmQgY29sb3IgOgoKYGBge3IgYmdfY29sb3J9CmJnX2NvbG9yID0gImdyYXk5NCIKYGBgCgoKVGhpcyBpcyB0aGUgY29ycmVzcG9uZGVuY2UgYmV0d2VlbiBjZWxsIHR5cGVzIGFuZCBjZWxsIGZhbWlsaWVzLCBhbmQgY3VzdG9tIGNvbG9ycyB0byBjb2xvciBjZWxscyBieSBjZWxsIGNhdGVnb3J5IDoKCmBgYHtyIGNlbGxfY2F0ZWdvcnl9CmN1c3RvbV9vcmRlcl9jZWxsX3R5cGUgPSBkYXRhLmZyYW1lKAogIGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpLAogIGNlbGxfY2F0ZWdvcnkgPSBjKHJlcCgiaW1tdW5lIGNlbGxzIiwgNSksCiAgICAgICAgICAgICAgICAgICAgcmVwKCJtYXRyaXgiLCA1KSwKICAgICAgICAgICAgICAgICAgICByZXAoIm5vbiBtYXRyaXgiLCA1KSksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZSA9IGZhY3RvcihjdXN0b21fb3JkZXJfY2VsbF90eXBlJGNlbGxfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjZWxsX3R5cGUpCnJvd25hbWVzKGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUpID0gY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjZWxsX3R5cGUKCmNhdGVnb3J5X2NvbG9yID0gYygiaW1tdW5lIGNlbGxzIiA9ICJzbGF0ZWJsdWUxIiwKICAgICAgICAgICAgICAgICAgICJtYXRyaXgiID0gIm1lZGl1bXNlYWdyZWVuIiwKICAgICAgICAgICAgICAgICAgICJub24gbWF0cml4IiA9ICJmaXJlYnJpY2szIikKYGBgCgpXZSBsb2FkIG1hcmtlcnMgdG8gZGlzcGxheSBvbiBhIGRvdHBsb3QgdG8gYXNzZXNzIGNlbGwgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIGRvdHBsb3RfbWFya2Vyc30KZG90cGxvdF9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9kb3RwbG90X21hcmtlcnMucmRzIikpCmRvdHBsb3RfbWFya2VycyA9IGRvdHBsb3RfbWFya2Vyc1tuYW1lcyhkb3RwbG90X21hcmtlcnMpICE9ICJtZWxhbm9jeXRlcyJdCmxlbmd0aHMoZG90cGxvdF9tYXJrZXJzKQpgYGAKCkN1c3RvbSBmdW5jdGlvbnMgdG8gZGlzcGxheSBnZW5lIGV4cHJlc3Npb24gb24gdGhlIGhlYXRtYXAgOgoKYGBge3IgY29sb3JfZnVuLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY29sb3JfZnVuID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBnZW5lX3JhbmdlID0gcmFuZ2UoaHRfYW5ub3RbLCBvbmVfZ2VuZV0pCiAgZ2VuZV9wYWxldHRlID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoY29sb3JzID0gYygiI0ZGRkZGRiIsIGFxdWFyaXVzOjpjb2xvcl9nZW5lWy0xXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSBnZW5lX3JhbmdlWzFdLCB0byA9IGdlbmVfcmFuZ2VbMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSBsZW5ndGgoYXF1YXJpdXM6OmNvbG9yX2dlbmUpKSkKICByZXR1cm4oZ2VuZV9wYWxldHRlKQp9CmBgYAoKCiMgQWxsIHNhbXBsZXMKCiMjIFNldHRpbmdzCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIGFsbF9zb2JqX25hbWUyRH0KbmFtZTJEID0gImhhcm1vbnlfMzhfdHNuZSIKbmFtZTJEX2F0bGFzID0gbmFtZTJECmBgYAoKIyMgUHJlcGFyYXRpb24KCldlIG1ha2UgYSBsb3cgcmVzb2x1dGl2ZSBjbHVzdGVyaW5nIGZvciB0aGUgaGVhdG1hcCA6CgpgYGB7ciBjbHVzdGVyX2FsbH0Kc29iaiA9IFNldXJhdDo6RmluZENsdXN0ZXJzKHNvYmosIHJlc29sdXRpb24gPSAwLjQpCgpsZW5ndGgobGV2ZWxzKHNvYmokc2V1cmF0X2NsdXN0ZXJzKSkKYGBgCgoKV2UgZGVmaW5lIGNsdXN0ZXIgdHlwZSBhbmQgY2x1c3RlciBjYXRlZ29yeSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfY2F0ZWdvcnlfYWxsLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0Kc29iaiRjZWxsX3R5cGUgPSBzb2JqJGNlbGxfdHlwZSAlPiUKICBhcy5jaGFyYWN0ZXIoKSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gbmFtZXMoY29sb3JfbWFya2VycykpCgpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmokY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmokc2V1cmF0X2NsdXN0ZXJzXQpzb2JqJGNsdXN0ZXJfdHlwZSA9IGZhY3Rvcihzb2JqJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGV2ZWxzKHNvYmokY2VsbF90eXBlKSkKc29iaiRjbHVzdGVyX2NhdGVnb3J5ID0gY3VzdG9tX29yZGVyX2NlbGxfdHlwZVtzb2JqJGNsdXN0ZXJfdHlwZSwgImNlbGxfY2F0ZWdvcnkiXQpzb2JqJGNsdXN0ZXJfY2F0ZWdvcnkgPSBmYWN0b3Ioc29iaiRjbHVzdGVyX2NhdGVnb3J5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbmFtZXMoY2F0ZWdvcnlfY29sb3IpKQpgYGAKCgojIyBGaWd1cmVzCgpQcm9qZWN0IG5hbWUgOgoKYGBge3IgZmlnMV9zYW1wbGVfaWRlbnRpZmllciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHByb2plY3RfbmFtZSA9IHNvYmokcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKClNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZzFfc2FtcGxlX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIEV4dHJhY3QgY29vcmRpbmF0ZXMKY2VsbHNfY29vcmQgPSBzb2JqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRzYW1wbGVfdHlwZSA9IHNvYmokc2FtcGxlX3R5cGUKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gc2FtcGxlX3R5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMC41KSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV90eXBlX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2x1c3RlciA6CgpgYGB7ciBmaWcxX2NsdXN0ZXIsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpncmV5X3BhbGV0dGUgPSBzZXROYW1lcyhubSA9IGxldmVscyhzb2JqJHNldXJhdF9jbHVzdGVycyksCiAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiI0Q5RDlEOSIsIGxlbmd0aChsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpKSkpCmdyZXlfcGFsZXR0ZVtjKCI3IiwgIjE2IiwgIjEiLCAiMTIiLCAiMTEiLCAiMTAiLCAiMTUiKV0gPSAiI0JEQkRCRCIKZ3JleV9wYWxldHRlW2MoIjE2IiwgIjE0IiwgIjUiLCAiOSIpXSA9ICIjOTY5Njk2IgoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNCwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGNvbHMgPSBncmV5X3BhbGV0dGUsCiAgICAgICAgICAgICAgICBsYWJlbCA9IFRSVUUsIGxhYmVsLnNpemUgPSA2KSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNlbGwgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIGZpZzFfY2VsbF90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXIgY2F0ZWdvcnkgYW5ub3RhdGlvbiA6CgpgYGB7ciBmaWcxX2NsdXN0ZXJfY2F0ZWdvcnksIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA2fQpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl9jYXRlZ29yeSIsIGNvbHMgPSBjYXRlZ29yeV9jb2xvcikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDZWxsIHR5cGUgYW5ub3RhdGlvbiBzcGxpdCBieSBjb25kaXRpb24gOgoKYGBge3IgZmlnMV9jZWxsX3R5cGVfc3BsaXQsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA1fQpwbG90X2xpc3QgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAiY2VsbF90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2NvbG9yID0gY29sb3JfbWFya2VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2NvbG9yID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfY29sb3IgPSBiZ19jb2xvcikKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QpICYKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpHZW5lIGV4cHJlc3Npb24gdG8gYXNzZXNzIGFubm90YXRpb24gOgoKYGBge3IgZmlnMV9nZW5lX2NhdGVnb3J5LCBmaWcud2lkdGggPSA0LjUsIGZpZy5oZWlnaHQgPSA0fQpnZW5lcyA9IGMoIlBUUFJDIiwgIk1TWDIiLCAiS1JUMTQiLAogICAgICAgICAgIyBGb3Igc3VwcGxlbWVudGFyeSBmaWd1cmUKICAgICAgICAgICJWSU0iLCAiRkdGMTgiLCAiU09YOSIsICJUQ0YzIiwgIlRCWDEiLAogICAgICAgICAgIlJVTlgxIiwgIk5GQVRDMSIsICJMSFgyIiwgIkNEMzQiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgcCA9IFNldXJhdDo6RmVhdHVyZVBsb3Qoc29iaiwgZmVhdHVyZXMgPSBnZW5lLCByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDA6NikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCiAgCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZ2VuZXMKcGxvdF9saXN0CmBgYAoKQmFycGxvdCBieSBjbHVzdGVyIGNhdGVnb3J5IDoKCmBgYHtyIGZpZzFfYmFycGxvdF9jYXRlZ29yeSwgZmlnLndpZHRoID0gMy41LCBmaWcuaGVpZ2h0ID0gMy41fQpxdWFudGlmID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAibmJfY2VsbHMiKSkKCmFxdWFyaXVzOjpwbG90X2JhcnBsb3QoZGYgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iaiRjbHVzdGVyX2NhdGVnb3J5KSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbCBUeXBlIiwgIk51bWJlciIpKSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiTnVtYmVyIiwgZmlsbCA9ICJDZWxsIFR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gLmRhdGEkU2FtcGxlLCB5ID0gMS4wNSwgbGFiZWwgPSAuZGF0YSRuYl9jZWxscyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDQpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY2F0ZWdvcnlfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNhdGVnb3J5X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgY2F0ZWdvcnkiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgYnkgPSAyNSksIHNlcCA9ICIgJSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IDI1LCByID0gMCwgYiA9IDAsIGwgPSAwKSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCkhlYXRtYXAgb2YgY2x1c3RlciBwcm9wb3J0aW9uIGJ5IHNhbXBsZSA6CgpgYGB7ciBmaWcxX2hlYXRtYXBfcHJvcCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpncm91cF9ieSA9ICJzZXVyYXRfY2x1c3RlcnMiCgpjbHVzdGVyX2J5X3NhbXBsZSA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc29iakBtZXRhLmRhdGFbLCBncm91cF9ieV0pICU+JQogIHByb3AudGFibGUobWFyZ2luID0gMSkgJT4lCiAgYXMubWF0cml4KCkKCiMjIFJpZ2h0IGFubm90YXRpb24gOiBudW1iZXIgb2YgY2VsbHMgYnkgZGF0YXNldApodF9hbm5vdCA9IHRhYmxlKHNvYmokc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygic2FtcGxlX2lkZW50aWZpZXIiLCAibmJfY2VsbHMiKSkgJT4lCiAgYHJvd25hbWVzPC1gKC4kc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGRwbHlyOjpzZWxlY3QoLXNhbXBsZV9pZGVudGlmaWVyKQoKaGFfcmlnaHQgPSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oCiAgZGYgPSBodF9hbm5vdCwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QobmJfY2VsbHMgID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiR3JleXMiLCBuID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoZnJvbSA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IHJhbmdlKGh0X2Fubm90JG5iX2NlbGxzKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gOSkpKSkKCiMjIExlZnQgYW5ub3RhdGlvbiA6IGdlbmRlcgpoYV9sZWZ0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGdlbmRlciA9IHNhbXBsZV9pbmZvJGdlbmRlciwKICB3aGljaCA9ICJyb3ciLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogIGNvbCA9IGxpc3QoZ2VuZGVyID0gc2V0TmFtZXMobm0gPSBjKCJGIiwgIk0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImxpZ2h0Y3lhbjMiLCAibmF2eWJsdWUiKSkpKQoKIyMgVG9wIGFubm90YXRpb24gOiBtYWluIGNlbGwgdHlwZSBpbiB0aGlzIGNsdXN0ZXIKaHRfYW5ub3QgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKG1hcmdpbiA9IDEpICU+JQogIGFzLm1hdHJpeCgpCgpodF9hbm5vdCA9IHRhYmxlKHNvYmokY2VsbF90eXBlLAogICAgICAgICAgICAgICAgIHNvYmpAbWV0YS5kYXRhWywgZ3JvdXBfYnldKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKaHRfYW5ub3QgPSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IG5hbWVzKGh0X2Fubm90KSwKICAgICAgICAgICAgICAgICAgICAgIGNlbGxfdHlwZSA9IG5hbWVzKGNvbG9yX21hcmtlcnMpW2h0X2Fubm90XSwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKaHRfYW5ub3QgPSBkcGx5cjo6bGVmdF9qb2luKGh0X2Fubm90LCBjdXN0b21fb3JkZXJfY2VsbF90eXBlLCBieSA9ICJjZWxsX3R5cGUiKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBtYXRyaXgKICBkcGx5cjo6bXV0YXRlKGNlbGxfdHlwZSA9IGlmZWxzZShjZWxsX3R5cGUgJWluJSBjKCJtZWR1bGxhIiwgImNvcnRleCIsICJjdXRpY2xlIiksIHllcyA9ICJoYWlyIHNoYWZ0Iiwgbm8gPSBjZWxsX3R5cGUpKSAlPiUKICAjIFNpbXBsaWZpY2F0aW9uIGZvciBUIGNlbGxzCiAgZHBseXI6Om11dGF0ZShjZWxsX3R5cGUgPSBpZmVsc2UoY2VsbF90eXBlICVpbiUgYygiQ0Q0IFQgY2VsbHMiLCAiQ0Q4IFQgY2VsbHMiKSwgeWVzID0gIlQgY2VsbHMiLCBubyA9IGNlbGxfdHlwZSkpICU+JQogICMgU2ltcGxpZmljYXRpb24gZm9yIEFQQwogIGRwbHlyOjptdXRhdGUoY2VsbF90eXBlID0gaWZlbHNlKGNlbGxfdHlwZSAlaW4lIGMoIkxhbmdlcmhhbnMgY2VsbHMiLCAibWFjcm9waGFnZXMiKSwgeWVzID0gIkFQQyIsIG5vID0gY2VsbF90eXBlKSkgJT4lCiAgIyBBZGQgY29sb3IKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gYXMuY2hhcmFjdGVyKGNvbG9yX21hcmtlcnNbY2VsbF90eXBlXSkpICU+JQogIGRwbHlyOjptdXRhdGUoY29sb3IgPSBpZmVsc2UoY2VsbF90eXBlID09ICJoYWlyIHNoYWZ0IiwgeWVzID0gIiNGRkI2QzEiLCBubyA9IGNvbG9yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb2xvciA9IGlmZWxzZShjZWxsX3R5cGUgPT0gIlQgY2VsbHMiLCB5ZXMgPSAiIzhBNkVFNiIsIG5vID0gY29sb3IpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNvbG9yID0gaWZlbHNlKGNlbGxfdHlwZSA9PSAiQVBDIiwgeWVzID0gIiM5Q0FBNEIiLCBubyA9IGNvbG9yKSkKCmhhX3RvcCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICAjIGNlbGxfdHlwZSA9IGh0X2Fubm90JGNlbGxfdHlwZSwKICBjZWxsX2NhdGVnb3J5ID0gaHRfYW5ub3QkY2VsbF9jYXRlZ29yeSwKICB3aGljaCA9ICJjb2x1bW4iLAogIHNob3dfbGVnZW5kID0gVFJVRSwKICBzaG93X2Fubm90YXRpb25fbmFtZSA9IEZBTFNFLAogICMgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAibGVmdCIsCiAgY29sID0gbGlzdCgjY2VsbF90eXBlID0gc2V0TmFtZXMobm0gPSBodF9hbm5vdCRjZWxsX3R5cGUsCiAgICAjICAgICAgICAgICAgICAgICAgICAgIGh0X2Fubm90JGNvbG9yKSwKICAgIGNlbGxfY2F0ZWdvcnkgPSBjYXRlZ29yeV9jb2xvcgogICkpCgojIyBBc3NlbWJsZSBoZWF0bWFwCmh0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoY2x1c3Rlcl9ieV9zYW1wbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIlByb3BvcnRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjKCIjMjE2NkFDIiwgIiNGN0Y3RjciLCAiI0IyMTgyQiIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGJvdHRvbV9hbm5vdGF0aW9uID0gaGFfYm90dG9tLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0X2Fubm90YXRpb24gPSBoYV9yaWdodCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2Fubm90YXRpb24gPSBoYV9sZWZ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGUgPSAiU2FtcGxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKG5hbWVzID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiQ2x1c3RlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX2NlbnRlcmVkID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfc2lkZSA9ICJ0b3AiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19yb3QgPSAwKQoKIyMgRHJhdyAhCkNvbXBsZXhIZWF0bWFwOjpkcmF3KGh0LCBtZXJnZV9sZWdlbmRzID0gVFJVRSkKYGBgCgpGb3IgdGhlIGRvdHBsb3QsIHdlIGNsYXJpZnkgY2x1c3RlcnMgYW5kIGNlbGwgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIGltcHJvdmVfY3VzdG9tX29yZGVyX2NlbGxfdHlwZX0KY2VsbF90eXBlX2luX2NsdXN0ZXIgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMSkgJT4lCiAgYXBwbHkoLiwgMSwgd2hpY2gubWF4KQpjZWxsX3R5cGVfaW5fY2x1c3RlciA9IGNlbGxfdHlwZV9pbl9jbHVzdGVyIC0gMQoKbWlzc2luZ19jbHVzdGVyID0gc2V0ZGlmZihsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfdHlwZV9pbl9jbHVzdGVyKQoKY2VsbF90eXBlX2luX2NsdXN0ZXIgPSBkYXRhLmZyYW1lKGNlbGxfdHlwZSA9IGMobmFtZXMoY2VsbF90eXBlX2luX2NsdXN0ZXIpLCBjbHVzdGVyX3R5cGVbbWlzc2luZ19jbHVzdGVyXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2lkID0gYyhjZWxsX3R5cGVfaW5fY2x1c3RlciwgbmFtZXMoY2x1c3Rlcl90eXBlW21pc3NpbmdfY2x1c3Rlcl0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgcm93Lm5hbWVzID0gTlVMTCkgJT4lCiAgZHBseXI6Om11dGF0ZShjbHVzdGVyX2lkID0gYXMubnVtZXJpYyhjbHVzdGVyX2lkKSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoY2VsbF90eXBlLCBjbHVzdGVyX2lkKQoKY3VzdG9tX29yZGVyX2NlbGxfdHlwZSRjbHVzdGVycyA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGUgJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgY2VsbF90eXBlID0gb25lX3Jvd1siY2VsbF90eXBlIl0KICAgIGNsdXN0ZXJzID0gY2VsbF90eXBlX2luX2NsdXN0ZXIgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIoLmRhdGEkY2VsbF90eXBlID09IC5lbnYkY2VsbF90eXBlKSAlPiUKICAgICAgZHBseXI6OnB1bGwoY2x1c3Rlcl9pZCkKICAgIAogICAgY2VsbF90eXBlX2NsdXN0ZXIgPSBwYXN0ZTAoY2VsbF90eXBlLCAiICgiLCBwYXN0ZTAoY2x1c3RlcnMsIGNvbGxhcHNlID0gIiwgIiksICIpIikKICAgIAogICAgcmV0dXJuKGNlbGxfdHlwZV9jbHVzdGVyKQogIH0pICU+JQogIGZhY3RvciguLCBsZXZlbHMgPSAuKQoKY3VzdG9tX29yZGVyX2NlbGxfdHlwZQpgYGAKCkRvdHBsb3QgOgoKYGBge3IgZmlnMV9kb3RwbG90LCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOC41LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KcGxvdF9saXN0ID0gYXF1YXJpdXM6OnBsb3RfZG90cGxvdChzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmtlcnMgPSBjKCJQVFBSQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNEM0UiLCAiQ0Q0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0QzRSIsICJDRDhBIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0QyMDciLCAiQUlGMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRSRU0yIiwgIk1TUjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDc5QSIsICJDRDc5QiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAiUFJETTEiLCAiS1JUODUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNU1gyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMzIiLCAiS1JUMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQzMSIsICJQUlI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQkFNQkkiLCAiQUxESDFBMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDcxIiwgIktSVDczIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVE9QMkEiLCAiTUNNNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE0IiwgIkNYQ0wxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE1IiwgIkNPTDE3QTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJESU8yIiwgIlRDRUFMMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDE2IiwgIktSVDZDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU1BJTks1IiwgIkxZNkQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDTE1QIiwgIlBQQVJHIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwgY29sdW1uX25hbWUgPSAiY2VsbF90eXBlIiwgbmJfaGxpbmUgPSAwKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImxlZnQiLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oMCw3MCwwLDApLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChyZXAoMCwgNCksICJjbSIpKQoKcCA9IGdncGxvdDI6OmdncGxvdChjdXN0b21fb3JkZXJfY2VsbF90eXBlLCBhZXMoeCA9IGNsdXN0ZXJzLCB5ID0gMCkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh4ID0gMC41LCB4ZW5kID0gNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gY2F0ZWdvcnlfY29sb3JbImltbXVuZSBjZWxscyJdKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGFlcyh4ID0gNS41LCB4ZW5kID0gMTAuNSwgeSA9IDAsIHllbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeCA9IDEwLjUsIHhlbmQgPSAxNS41LCB5ID0gMCwgeWVuZCA9IDApLCBzaXplID0gNiwgY29sID0gY2F0ZWdvcnlfY29sb3JbIm5vbiBtYXRyaXgiXSkgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGxpbWl0cyA9IGMoMCwwKSkgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gMTAsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLjUsMC41LDApLCAiY20iKSkKCnBsb3RfbGlzdCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMSwgaGVpZ2h0cyA9IGMoMjUsIDEpKQpwbG90X2xpc3QKYGBgCgoKIyBJbW11bmUgY2VsbHMKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBpbW11bmUgY2VsbHMgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2ljfQpzb2JqX2ljID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzFfem9vbV9pbW11bmUvaW1tdW5lX2NlbGxzX3NvYmoucmRzIikpCnNvYmpfaWMKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF9pY30KbmFtZTJEID0gImhhcm1vbnlfMjBfdHNuZSIKYGBgCgpUbyByZXByZXNlbnQgcmVzdWx0cyBmcm9tIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSBsb2FkIHRoZSBhbmFseXNlcyByZXN1bHRzIDoKCmBgYHtyIGxpc3RfcmVzdWx0c19pY30KbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzFfem9vbV9pbW11bmUvaW1tdW5lX2NlbGxzX2xpc3RfcmVzdWx0cy5yZHMiKSkKCmxhcHBseShsaXN0X3Jlc3VsdHMsIEZVTiA9IG5hbWVzKQpgYGAKCgojIyBQcmVwYXJhdGlvbgoKV2UgZGVmaW5lZCBjbHVzdGVyIHR5cGUgYW5kIGNsdXN0ZXIgY2F0ZWdvcnkgOgoKYGBge3IgY2x1c3Rlcl90eXBlX2NhdGVnb3J5X2ljLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYyRjZWxsX3R5cGUsIHNvYmpfaWMkc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICBwcm9wLnRhYmxlKC4sIG1hcmdpbiA9IDIpICU+JQogIGFwcGx5KC4sIDIsIHdoaWNoLm1heCkKY2x1c3Rlcl90eXBlID0gc2V0TmFtZXMobm0gPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMoc29ial9pYyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2ljJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqX2ljJHNldXJhdF9jbHVzdGVyc10Kc29ial9pYyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYyRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGxldmVscyhzb2JqX2ljJGNlbGxfdHlwZSkpCmBgYAoKCiMjIEZpZ3VyZXMKCkltbXVuZSBjZWxscyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdfaWNfbG9jYXRpb24sIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqJGlzX2ltbXVuZSA9IChjb2xuYW1lcyhzb2JqKSAlaW4lIGNvbG5hbWVzKHNvYmpfaWMpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaW1tdW5lIiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjYXRlZ29yeV9jb2xvcltbImltbXVuZSBjZWxscyJdXSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKFRSVUUsIEZBTFNFKSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiSW1tdW5lIGNlbGxzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9pYyksICIgY2VsbHMiKSkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKQ2x1c3RlciB0eXBlIDoKCmBgYHtyIGZpZ19pY19jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYywgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXJpbmcKCmBgYHtyIGZpZ19pY19jbHVzdGVyaW5nLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmpfaWMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBsYWJlbCA9IFRSVUUpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKVGFibGUgdG8gbWFrZSB2aW9saW4gcGxvdCBieSBzYW1wbGUsIGJ5IHNhbXBsZSB0eXBlLCBhbmQgZmVhdHVyZSBwbG90IHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIHRhYmxlX2ljfQpsaXN0X2NsdXN0ZXJzID0gbGlzdChhbGwgPSBuYW1lcyhjbHVzdGVyX3R5cGUpLAogICAgICAgICAgICAgICAgICAgICBtYWNyb3BoYWdlcyA9IDIsCiAgICAgICAgICAgICAgICAgICAgIENENF9UX2NlbGxzID0gbmFtZXMoY2x1c3Rlcl90eXBlW2NsdXN0ZXJfdHlwZSA9PSAiQ0Q0IFQgY2VsbHMiXSksCiAgICAgICAgICAgICAgICAgICAgIENEOF9UX2NlbGxzID0gbmFtZXMoY2x1c3Rlcl90eXBlW2NsdXN0ZXJfdHlwZSA9PSAiQ0Q4IFQgY2VsbHMiXSkpCmxpc3Rfc3Vic29iaiA9IGxhcHBseShsaXN0X2NsdXN0ZXJzLCBGVU4gPSBmdW5jdGlvbihjbHVzdGVycykgewogIHNvYmpfaWMkc2V1cmF0X2NsdXN0ZXJzID0gYXMuY2hhcmFjdGVyKHNvYmpfaWMkc2V1cmF0X2NsdXN0ZXJzKQogIAogICMgRXh0cmFjdCBjZWxscyBvZiBpbnRlcmVzdAogIHN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgc2V1cmF0X2NsdXN0ZXJzICVpbiUgY2x1c3RlcnMpCiAgCiAgcmV0dXJuKHN1YnNvYmopCn0pCm5hbWVzKGxpc3Rfc3Vic29iaikgPSBuYW1lcyhsaXN0X2NsdXN0ZXJzKQpybShsaXN0X2NsdXN0ZXJzKQoKdGFibGVfaWMgPSBkYXRhLmZyYW1lKGNsdXN0ZXJzID0gYygiYWxsIiwgcmVwKCJtYWNyb3BoYWdlcyIsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiQ0Q0X1RfY2VsbHMiLCAzKSwgcmVwKCJDRDhfVF9jZWxscyIsIDIpKSwKICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmUgPSBjKCJJTDYiLCAiSUwxQiIsICJUTkYiLCAiR1pNQSIsICJJRk5HIiwgIklMMTdBIiwgIlBSRjEiLCAiR1pNQiIpKQp0YWJsZV9pYwpgYGAKClZpb2xpbiBwbG90cyBzcGxpdCBieSBzYW1wbGUgOgoKYGBge3IgZmlnX2ljX3Zsbl9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnBsb3RfbGlzdCA9IGFwcGx5KHRhYmxlX2ljLCAxLCBGVU4gPSBmdW5jdGlvbihvbmVfcm93KSB7CiAgIyBJbnB1dAogIHBvcHVsYXRpb24gPSBvbmVfcm93WzFdCiAgc3Vic29iaiA9IGxpc3Rfc3Vic29ialtbcG9wdWxhdGlvbl1dCiAgZmVhdHVyZSA9IG9uZV9yb3dbMl0KICAKICAjIE1ha2UgZmlndXJlCiAgcCA9IFNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmUsIGNvbHMgPSBzYW1wbGVfaW5mbyRjb2xvcikgKwogICAgZ2dwbG90Mjo6bGFicyhzdWJ0aXRsZSA9IHBvcHVsYXRpb24pICsKICAgIGdncGxvdDI6OnRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogIHJldHVybihwKQp9KQoKbmFtZXMocGxvdF9saXN0KSA9IHRhYmxlX2ljJGZlYXR1cmUKcGxvdF9saXN0CmBgYAoKRmVhdHVyZSBwbG90cyBzcGxpdCBieSBzYW1wbGUgdHlwZSA6CgpgYGB7ciBmaWdfaWNfZmVhdHVyZV9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDN9CnBsb3RfbGlzdCA9IGxhcHBseSh0YWJsZV9pYyRmZWF0dXJlLCBGVU4gPSBmdW5jdGlvbihvbmVfZ2VuZSkgewogIHAgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29ial9pYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInNhbXBsZV90eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2J5ID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9wYWxldHRlID0gYygiZ3JheTcwIiwgIiNGREJCODQiLCAiI0VGNjU0OCIsICIjN0YwMDAwIiwgImJsYWNrIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluX3B0X3NpemUgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfY29sb3IgPSAiZ3JheTk1IikKICBwID0gcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHAsIG5yb3cgPSAxKSArCiAgICBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgKwogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgJgogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSB0YWJsZV9pYyRmZWF0dXJlCnBsb3RfbGlzdApgYGAKClZpb2xpbiBwbG90cyBzcGxpdCBieSBzYW1wbGUgdHlwZSA6CgpgYGB7ciBmaWdfaWNfdmxuX3NhbXBsZV90eXBlLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnBsb3RfbGlzdCA9IGFwcGx5KHRhYmxlX2ljLCAxLCBGVU4gPSBmdW5jdGlvbihvbmVfcm93KSB7CiAgIyBJbnB1dAogIHBvcHVsYXRpb24gPSBvbmVfcm93WzFdCiAgc3Vic29iaiA9IGxpc3Rfc3Vic29ialtbcG9wdWxhdGlvbl1dCiAgZmVhdHVyZSA9IG9uZV9yb3dbMl0KICAKICAjIHQtdGVzdCBiZXR3ZWVuIHNhbXBsZSB0eXBlCiAgZmVhdHVyZV9leHByID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbZmVhdHVyZSwgXQogIGZlYXR1cmVfaHMgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQogIGZlYXR1cmVfaGQgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCA9IHN0YXRzOjp0LnRlc3QoZmVhdHVyZV9ocywgZmVhdHVyZV9oZCkKICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQKICAKICAjIFNpZ25pZmljYW5jZSBhc3NvY2lhdGVkIHdpdGggcC12YWx1ZQogIHB2YWwgPSBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQkcC52YWx1ZQogIAogIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbihwdmFsID4gMC4wNSB+ICJucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAxICYgcHZhbCA8PSAwLjA1IH4gIioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMDEgJiBwdmFsIDw9IDAuMDEgfiAiKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsIDw9IDAuMDAxIH4gIioqKiIpCiAgCiAgIyBQbG90CiAgcCA9IFNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmUpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV90eXBlX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICAgIGdncGxvdDI6OmxhYnMoc3VidGl0bGUgPSBwb3B1bGF0aW9uKSArCiAgICAjIFNpZ25pZmljYW5jZSBiYXIKICAgIGdncGxvdDI6Omdlb21fc2VnbWVudChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAxLCB4ZW5kID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zLCB5ZW5kID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICB4ID0gMS41LCB5ID0gbWF4KGZlYXR1cmVfZXhwcikrMC4zNSwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzaWduaWZpY2FuY2UsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgbGFiZWwuc2l6ZSA9IDApICsKICAgIGdncGxvdDI6OmxpbXMoeSA9IGMoMCwgbWF4KGZlYXR1cmVfZXhwcikrMC40KSkgKwogICAgIyBUaGVtZQogICAgZ2dwbG90Mjo6dGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gdGFibGVfaWMkZmVhdHVyZQpwbG90X2xpc3QKYGBgCgpCYXJwbG90IGJ5IGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBmaWdfaWNfYmFycGxvdCwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpxdWFudGlmX3RvdGFsID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAibmJfY2VsbHMiKSkKCiMgTnVtYmVyIG9mIGN5dG90b3hpYyBUIGNlbGxzCnF1YW50aWZfY3l0b3RveCA9IHNvYmpfaWNAbWV0YS5kYXRhICU+JQogIGRwbHlyOjptdXRhdGUoY3l0b3RveGljID0gaWZlbHNlKHNldXJhdF9jbHVzdGVycyAlaW4lIGMoIjUiLCI2IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gIkN5dG90b3hpYyBDRDggVCBjZWxscyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBhcy5jaGFyYWN0ZXIoY2x1c3Rlcl90eXBlKSkpICU+JQogICMgY2VsbHMgb2YgaW50ZXJlc3QKICBkcGx5cjo6ZmlsdGVyKGN5dG90b3hpYyA9PSAiQ3l0b3RveGljIENEOCBUIGNlbGxzIikgJT4lCiAgIyBjb3VudCBieSBzYW1wbGUKICBkcGx5cjo6c2VsZWN0KHNhbXBsZV9pZGVudGlmaWVyLCBjbHVzdGVyX3R5cGUpICU+JQogIGJhc2U6OmRyb3BsZXZlbHMoKSAlPiUKICB0YWJsZSgpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGxUeXBlIiwgIk51bWJlciIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGlzQ3l0b3RveGljID0gImN5dG90b3hpYyIpCiMgTnVtYmVyIG9mIGltbXVuZSBjZWxscwpxdWFudGlmX2ltbXVuZSA9IHRhYmxlKHNvYmpfaWMkc2FtcGxlX2lkZW50aWZpZXIsIHNvYmpfaWMkY2x1c3Rlcl90eXBlKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJDZWxsVHlwZSIsICJOdW1iZXIiKSkgJT4lCiAgZHBseXI6OmZpbHRlcihDZWxsVHlwZSAlaW4lIGMoIkNENCBUIGNlbGxzIiwgIkNEOCBUIGNlbGxzIiwgIm1hY3JvcGhhZ2VzIikpICU+JQogIGRwbHlyOjptdXRhdGUoaXNDeXRvdG94aWMgPSAibm8iKSAlPiUKICAjIGpvaW4gY3l0b3RveGljCiAgcmJpbmQoLiwgcXVhbnRpZl9jeXRvdG94KSAlPiUKICAjIHByb3BvcnRpb24gZm9yIHRoZSB3aG9sZSBkYXRhc2V0CiAgZHBseXI6OmxlZnRfam9pbiguLCBxdWFudGlmX3RvdGFsLCBieSA9ICJTYW1wbGUiKSAlPiUKICBkcGx5cjo6bXV0YXRlKHByb3BfY2VsbHMgPSAxMDAqcm91bmQoTnVtYmVyL25iX2NlbGxzLDQpKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGxUeXBlIiwgIk51bWJlciIsICJpc0N5dG90b3hpYyIsICJUb3RhbCIsICJQcm9wb3J0aW9uIikpICU+JQogIGRwbHlyOjptdXRhdGUoaXNDeXRvdG94aWMgPSBmYWN0b3IoaXNDeXRvdG94aWMsIGxldmVscyA9IGMoImN5dG90b3hpYyIsICJubyIpKSkKCmdncGxvdDI6OmdncGxvdChkYXRhID0gcXVhbnRpZl9pbW11bmUgJT4lIGRwbHlyOjpmaWx0ZXIoaXNDeXRvdG94aWMgIT0gImN5dG90b3hpYyIpLAogICAgICAgICAgICAgICAgYWVzKHggPSBTYW1wbGUsIHkgPSBQcm9wb3J0aW9uLCBmaWxsID0gQ2VsbFR5cGUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjUpLAogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUsIHdpZHRoID0gMC40KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnNbYygiQ0Q0IFQgY2VsbHMiLCAiQ0Q4IFQgY2VsbHMiLCAibWFjcm9waGFnZXMiKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnNbYygiQ0Q0IFQgY2VsbHMiLCAiQ0Q4IFQgY2VsbHMiLCAibWFjcm9waGFnZXMiKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBUeXBlIikgKwogICMgT3ZlcmxheSBhIGxheWVyIGZvciBzdHJpcGUKICAjIGdncGxvdDI6Omdlb21fYmFyKGRhdGEgPSBxdWFudGlmX2ltbXVuZSAlPiUgZHBseXI6OmZpbHRlcihpc0N5dG90b3hpYyA9PSAiY3l0b3RveGljIiksCiAgIyAgICAgICAgICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSksIGZpbGwgPSAiYmxhY2siLAogICMgICAgICAgICAgICAgICAgICAgY29sb3IgPSAibGlnaHRncmF5Iiwgc2l6ZSA9IDAuMjUsIHdpZHRoID0gMC40LzMsIGFlcyhwYXR0ZXJuID0gaXNDeXRvdG94aWMpKSArCiAgIyBnZ3BhdHRlcm46Omdlb21fY29sX3BhdHRlcm4oZGF0YSA9IHF1YW50aWZfaW1tdW5lICU+JSBkcGx5cjo6ZmlsdGVyKGlzQ3l0b3RveGljID09ICJjeXRvdG94aWMiKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuX2RlbnNpdHkgPSAwLjAwMSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuX3NwYWNpbmcgPSAwLjAxNSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuX2tleV9zY2FsZV9mYWN0b3IgPSAxLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuMjUsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAwLjM3LzMsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybl9jb2xvciA9ICJibGFjayIsICMgYmFyIGJvcmRlcgojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IFRSVUUpICsKIyBnZ3BhdHRlcm46OnNjYWxlX3BhdHRlcm5fbWFudWFsKG5hbWUgPSAiQ0Q4IFQgY2VsbHMiLCB2YWx1ZXMgPSAgInN0cmlwZSIpICsKIyBnZ3BhdHRlcm46OnNjYWxlX3BhdHRlcm5fYW5nbGVfbWFudWFsKG5hbWUgPSAiQ0Q4IFQgY2VsbHMiLCB2YWx1ZXMgPSA1MCkgKwojIGdncGxvdDI6Omd1aWRlcyhwYXR0ZXJuID0gZ2dwbG90Mjo6Z3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoZmlsbCA9ICJ3aGl0ZSIpKSwKIyAgICAgICAgICAgICAgICAgZmlsbCA9IGdncGxvdDI6Omd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHBhdHRlcm4gPSAibm9uZSIpKSkgKwojIFRoZW1lCmdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIG1heChxdWFudGlmX2ltbXVuZSRQcm9wb3J0aW9uKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAxKSkpICsKICBnZ3Bsb3QyOjpsYWJzKHkgPSAiJSBvZiBpbW11bmUgY2VsbHMgYnkgc2FtcGxlIikgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJsaWdodGdyYXkiKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImxpZ2h0Z3JheSIsIHNpemUgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJsaWdodGdyYXkiLCBzaXplID0gMC41KSkKYGBgCgpIZWF0bWFwIGZvciBtYWNyb3BoYWdlcyA6CgpgYGB7ciBmaWdfaWNfaGVhdG1hcF9tYWNyb3BoYWdlcywgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDUuMiwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgY2x1c3Rlcl90eXBlID09ICJtYWNyb3BoYWdlcyIpCmZlYXR1cmVzX29pID0gYygiSUwxQiIsICJUTkYiLAogICAgICAgICAgICAgICAgIkhMQS1EUUEyIiwgIkhMQS1EUEExIiwgIkhMQS1EUkI1IiwKICAgICAgICAgICAgICAgICJITEEtQSIsICJITEEtQyIsICJCMk0iLAogICAgICAgICAgICAgICAgIkMxUUEiLCAiQzFRQiIsICJDMVFDIikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0pKQoKIyBIZWF0bWFwCmh0ID0gSGVhdG1hcChhcy5tYXRyaXgobWF0X2V4cHIpLAogICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkV4cHJlc3Npb24iLCBhdCA9IGMoMCwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImxvdyIsICJoaWdoIikpLAogICAgICAgICAgICAgY29sID0gbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dLAogICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd190aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTQsIGZvbnRmYWNlID0gInBsYWluIiksCiAgICAgICAgICAgICB1c2VfcmFzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpCmBgYAoKSGVhdG1hcCBmb3IgQ0Q0IFQgY2VsbHMgOgoKYGBge3IgZmlnX2ljX2hlYXRtYXBfY2Q0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYywgY2x1c3Rlcl90eXBlID09ICJDRDQgVCBjZWxscyIpCmZlYXR1cmVzX29pID0gYygiR1pNQSIsICJLTFJCMSIsICJCVEcxIiwgIlpGUDM2IiwgIk5GS0JJQSIsICJUWE5JUCIsICJDWENSNCIsICJJRk5HIiwgIklMMTdBIikKCiMgTWF0cml4Cm1hdF9leHByID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc3Vic29iaikKbWF0X2V4cHIgPSBtYXRfZXhwcltmZWF0dXJlc19vaSwgXQptYXRfZXhwciA9IE1hdHJpeDo6dChtYXRfZXhwcikKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHNhbXBsZV90eXBlID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfdHlwZSJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaWRlbnRpZmllciA9IGxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0pKQoKIyBIZWF0bWFwCmh0ID0gSGVhdG1hcChhcy5tYXRyaXgobWF0X2V4cHIpLAogICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW0gPSBsaXN0KHRpdGxlID0gIkV4cHJlc3Npb24iLCBhdCA9IGMoMCwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoImxvdyIsICJoaWdoIikpLAogICAgICAgICAgICAgY29sID0gbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dLAogICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgY29sdW1uX29yZGVyID0gY29sdW1uX29yZGVyLAogICAgICAgICAgICAgY29sdW1uX2dhcCA9IHVuaXQoMiwgIm1tIiksCiAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgIHJvd190aXRsZSA9IE5VTEwsCiAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncmlkOjpncGFyKGZvbnRzaXplID0gMTQsIGZvbnRmYWNlID0gInBsYWluIiksCiAgICAgICAgICAgICB1c2VfcmFzdGVyID0gRkFMU0UsCiAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJsZWZ0IiwKICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmRfc2lkZSA9ICJsZWZ0IikKYGBgCgoKIyBIRlNDCgojIyBTZXR0aW5ncwoKV2UgbG9hZCB0aGUgSEZTQ3MgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2hmc2N9CnNvYmpfaGZzYyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8yX3pvb21faGZzYy9oZnNjX3NvYmoucmRzIikpCnNvYmpfaGZzYwpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX2hmc2N9Cm5hbWUyRCA9ICJoYXJtb255XzI0X3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaGZzY30KbGlzdF9yZXN1bHRzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzJfem9vbV9oZnNjL2hmc2NfbGlzdF9yZXN1bHRzLnJkcyIpKQoKbGFwcGx5KGxpc3RfcmVzdWx0cywgRlVOID0gbmFtZXMpCmBgYAoKIyMgRmlndXJlcwoKSEZTQ3Mgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX2hmc2NfbG9jYXRpb24sIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpzb2JqJGlzX2hmc2MgPSAoY29sbmFtZXMoc29iaikgJWluJSBjb2xuYW1lcyhzb2JqX2hmc2MpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaGZzYyIsIG9yZGVyID0gIlRSVUUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tbIkhGU0MiXV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhUUlVFLCBGQUxTRSkpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkhGU0NzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9oZnNjKSwgIiBjZWxscyIpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKyBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpLUlQxNSBleHByZXNzaW9uIDoKCmBgYHtyIGZpZ19oZnNjX2tydDE1LCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0KU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJLUlQxNSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkdlbmVzIG9mIGludGVyZXN0IDoKCmBgYHtyIGZpZ19oZnNjX2dlbmVzLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZXMgPSBjKCJUR0ZCMiIsICJBTkdQVEw3IiwgIkZHRjE4IiwgIk1HUCIsICJFUENBTSIsICJLUlQ3NSIsICJOT1RDSDMiLCAiUFRITEgiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9oZnNjJG15X2dlbmUgPSBTZXVyYXQ6OkZldGNoRGF0YShzb2JqX2hmc2MsIGdlbmUpWywgMV0gJT4lCiAgICBhcXVhcml1czo6cnVuX3Jlc2NhbGUoLiwgbmV3X21pbiA9IDAsIG5ld19tYXggPSAxMCkKICAKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmpfaGZzYywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCAxMCwgYnkgPSAyLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIm1pbiIsIHJlcCgiIiwgMyksICJtYXgiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IGdlbmUpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKClByb2plY3QgbmFtZSA6CgpgYGB7ciBmaWdfaGZzY19wcm9qZWN0X25hbWUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29ial9oZnNjKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqX2hmc2MpKQoKIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29ial9oZnNjQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqX2hmc2MkcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAxLjIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2x1c3RlciA6CgpgYGB7ciBmaWdfaGZzY19jbHVzdGVycywgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqX2hmc2MsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gNykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpIZWF0bWFwIHdpdGggcHJvcG9ydGlvbnMgOgoKYGBge3IgZmlnX2hmc2NfaGVhdG1hcCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNS41LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY2x1c3Rlcl9tYXJrZXJzID0gYygiVEdGQjIiLCAiQU5HUFRMNyIsICJFUENBTSIsICJLUlQ3NSIsICJOT1RDSDMiLCAiUFRITEgiKQoKIyMgQm90dG9tIGFubm90YXRpb24gOiBnZW5lIGV4cHJlc3Npb24gYnkgY2x1c3RlcgpodF9hbm5vdCA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaGZzYywgc2xvdCA9ICJkYXRhIiwgdmFycyA9IGNsdXN0ZXJfbWFya2VycykgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCmh0X2Fubm90JGNsdXN0ZXJzID0gc29ial9oZnNjJHNldXJhdF9jbHVzdGVycwpodF9hbm5vdCA9IGh0X2Fubm90ICU+JQogIGRwbHlyOjpncm91cF9ieShjbHVzdGVycykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZV9hbGwoZnVucygnbWVhbicgPSBtZWFuKSkgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGRwbHlyOjpzZWxlY3QoLWNsdXN0ZXJzKSAlPiUKICBgY29sbmFtZXM8LWAoYyhjbHVzdGVyX21hcmtlcnMpKQoKaGFfYm90dG9tID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gaHRfYW5ub3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aGljaCA9ICJjb2x1bW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gc2V0TmFtZXMobm0gPSBjbHVzdGVyX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXBwbHkoY2x1c3Rlcl9tYXJrZXJzLCBGVU4gPSBjb2xvcl9mdW4pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlID0gImxlZnQiKQoKIyMgUmlnaHQgYW5ub3RhdGlvbiA6IG51bWJlciBvZiBjZWxscyBieSBkYXRhc2V0Cmh0X2Fubm90ID0gdGFibGUoc29ial9oZnNjJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoInNhbXBsZV9pZGVudGlmaWVyIiwgIm5iX2NlbGxzIikpICU+JQogIGByb3duYW1lczwtYCguJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1zYW1wbGVfaWRlbnRpZmllcikKCmhhX3JpZ2h0ID0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKAogIGRmID0gaHRfYW5ub3QsCiAgd2hpY2ggPSAicm93IiwKICBzaG93X2xlZ2VuZCA9IFRSVUUsCiAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAiYm90dG9tIiwKICBjb2wgPSBsaXN0KG5iX2NlbGxzICA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIkdyZXlzIiwgbiA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKGZyb20gPSByYW5nZShodF9hbm5vdCRuYl9jZWxscylbMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSByYW5nZShodF9hbm5vdCRuYl9jZWxscylbMl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDkpKSkpCgojIyBIZWF0bWFwCmh0ID0gYXF1YXJpdXM6OnBsb3RfcHJvcF9oZWF0bWFwKGRmID0gc29ial9oZnNjQG1ldGEuZGF0YVssIGMoInNhbXBsZV9pZGVudGlmaWVyIiwgInNldXJhdF9jbHVzdGVycyIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBoYV9ib3R0b20sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcmlnaHRfYW5ub3RhdGlvbiA9IGhhX3JpZ2h0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXNfY2VudGVyZWQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wX21hcmdpbiA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIobmFtZXMgPSBzYW1wbGVfaW5mbyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZSA9ICJTYW1wbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiQ2x1c3RlciIpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iKQpgYGAKCkhlYXRtYXAgZm9yIGNsdXN0ZXIgMCBhbmQgOCA6CgpgYGB7ciBmaWdfaGZzY19oZWF0bWFwX2NsdXN0ZXIwOCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gMTAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaGZzYywgc2V1cmF0X2NsdXN0ZXJzICVpbiUgYygwLDgpKQoKZmVhdHVyZXNfb2kgPSByb3duYW1lcyhsaXN0X3Jlc3VsdHMkY2x1c3Rlcl8wXzgpCmZlYXR1cmVzX29pID0gZmVhdHVyZXNfb2lbIWdyZXBsKGZlYXR1cmVzX29pLCBwYXR0ZXJuID0gIl5SUCIpXQoKIyBNYXRyaXgKbWF0X2V4cHIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqKQptYXRfZXhwciA9IG1hdF9leHByW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQptYXRfZXhwciA9IGNiaW5kKG1hdF9leHByLCBzdWJzb2JqJHBlcmNlbnQubXQpCmNvbG5hbWVzKG1hdF9leHByKVtuY29sKG1hdF9leHByKV0gPSAicGVyY2VudC5yYiIKbWF0X2V4cHIgPSBkeW51dGlsczo6c2NhbGVfcXVhbnRpbGUobWF0X2V4cHIpICMgYmV0d2VlbiAwIGFuZCAxCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQpkaW0obWF0X2V4cHIpICMgZ2VuZXMgeCBjZWxscwojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKCiMgSGVhdG1hcApsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0gPSByZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG5hbWUgPSAiUmRCdSIsIG4gPSA5KSkKCiMgU2FtcGxlIGFubm90YXRpb24gKHRvcCBhbm5vdGF0aW9uKQpsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dID0gc2FtcGxlX3R5cGVfY29sb3JzCmxpc3RfY29sb3JzW1sic2FtcGxlX2lkZW50aWZpZXIiXV0gPSBzZXROYW1lcyhubSA9IHNhbXBsZV9pbmZvJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8kY29sb3IpCiMgbGlzdF9jb2xvcnNbWyJzZXVyYXRfY2x1c3RlcnMiXV0gPSBzZXROYW1lcyhhcXVhcml1czo6Z2dfY29sb3JfaHVlKGxlbmd0aChsZXZlbHMoc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMpKSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IGxldmVscyhzdWJzb2JqJHNldXJhdF9jbHVzdGVycykpCiMgQ2VsbHMgb3JkZXIKY29sdW1uX29yZGVyID0gc3Vic29iakBtZXRhLmRhdGEgJT4lCiAgZHBseXI6OmFycmFuZ2Uoc2FtcGxlX3R5cGUsIHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICByb3duYW1lcygpCmNvbHVtbl9vcmRlciA9IG1hdGNoKGNvbHVtbl9vcmRlciwgcm93bmFtZXMoc3Vic29iakBtZXRhLmRhdGEpKQoKIyBIZWF0bWFwCmhhX3RvcCA9IEhlYXRtYXBBbm5vdGF0aW9uKHNhbXBsZV90eXBlID0gc3Vic29iaiRzYW1wbGVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2lkZW50aWZpZXIgPSBzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNsdXN0ZXJzID0gc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2x1c3RlcnMgPSBsaXN0X2NvbG9yc1tbInNldXJhdF9jbHVzdGVycyJdXQogICAgICAgICAgICAgICAgICAgICAgICAgICApKQoKCiMgZzEgOiBSRUFDVE9NRV9DWVRPS0lORV9TSUdOQUxJTkdfSU5fSU1NVU5FX1NZU1RFTQojIGcyIDogR09CUF9BUE9QVE9USUNfUFJPQ0VTUwpnMV9nZW5lcyA9IGMoIkIyTSIsICJITEEtQyIsICJITEEtQSIsICJNSUYiLCAiUFBJQSIsICJKVU5CIiwgIklGSVRNMyIpCmcyX2dlbmVzID0gYygiSnVuIiwgIkFURjMiLCAiQlRHMiIsICJSSE9CIiwgIk5GS0JJQSIsICJTR0sxIiwgIktMRjkiLAogICAgICAgICAgICAgIkNBVjEiLCAiRERJVDQiLCAiUERLNCIsICJUWE5JUCIsICJSTkYxMTUyIiwgIlRMRTEiKQpoYV9yaWdodCA9IGRhdGEuZnJhbWUoZ2VuZXMgPSAgYyhmZWF0dXJlc19vaSwgInBlcmNlbnQucmIiKSwgcm93bmFtZXMgPSBjKGZlYXR1cmVzX29pLCAicGVyY2VudC5yYiIpKQpoYV9yaWdodCRncm91cCA9IGNhc2Vfd2hlbihoYV9yaWdodCRnZW5lcyAlaW4lIGcxX2dlbmVzIH4gIlJFQUNUT01FX0NZVE9LSU5FX1NJR05BTElOR19JTl9JTU1VTkVfU1lTVEVNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaGFfcmlnaHQkZ2VuZXMgJWluJSBnMl9nZW5lcyB+ICJHT0JQX0FQT1BUT1RJQ19QUk9DRVNTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJvdGhlcnMiKQoKbGlzdF9jb2xvcnNbWyJncm91cCJdXSA9IHNldE5hbWVzKAogIG5tID0gYygiUkVBQ1RPTUVfQ1lUT0tJTkVfU0lHTkFMSU5HX0lOX0lNTVVORV9TWVNURU0iLCAiR09CUF9BUE9QVE9USUNfUFJPQ0VTUyIsICJvdGhlcnMiKSwKICBjKCJyZWQiLCAiYmxhY2siLCAiZ3JheTkwIikpCgpoYV9yaWdodCA9IEhlYXRtYXBBbm5vdGF0aW9uKGdyb3VwID0gaGFfcmlnaHQkZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChncm91cCA9IGxpc3RfY29sb3JzW1siZ3JvdXAiXV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoID0gInJvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19hbm5vdGF0aW9uX25hbWUgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2xlZ2VuZCA9IFRSVUUpCgojIEhlYXRtYXAKaHQgPSBIZWF0bWFwKGFzLm1hdHJpeChtYXRfZXhwciksCiAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiRXhwcmVzc2lvbiIsIGF0ID0gYygwLCAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibG93IiwgImhpZ2giKSksCiAgICAgICAgICAgICBjb2wgPSBsaXN0X2NvbG9yc1tbImV4cHJlc3Npb24iXV0sCiAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhhX3RvcCwKICAgICAgICAgICAgIHJpZ2h0X2Fubm90YXRpb24gPSBoYV9yaWdodCwKICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBjb2x1bW5fb3JkZXIsCiAgICAgICAgICAgICBjb2x1bW5fZ2FwID0gdW5pdCgyLCAibW0iKSwKICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgcm93X3RpdGxlID0gTlVMTCwKICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSAxMCwgZm9udGZhY2UgPSAicGxhaW4iKSwKICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBGQUxTRSwKICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgYm9yZGVyID0gVFJVRSkKCkNvbXBsZXhIZWF0bWFwOjpkcmF3KGh0LAogICAgICAgICAgICAgICAgICAgICBtZXJnZV9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbGVnZW5kX3NpZGUgPSAiYm90dG9tIikKYGBgCgpWaW9saW4gcGxvdHMgc3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19oZnNjX3Zsbl9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CmZlYXR1cmVzX29pID0gYygiSUZJVE0zIiwgIkRESVQ0IikKCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24oZmVhdHVyZSkgewogIHAgPSBTZXVyYXQ6OlZsblBsb3Qoc3Vic29iaiwgZ3JvdXAuYnkgPSAic2FtcGxlX2lkZW50aWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBmZWF0dXJlLCBjb2xzID0gc2FtcGxlX2luZm8kY29sb3IpICsKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBmZWF0dXJlc19vaQpwbG90X2xpc3QKYGBgCgpGZWF0dXJlIHBsb3RzIHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZ19oZnNjX2ZlYXR1cmVfc3BsaXQsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSAzfQpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXNfb2ksIEZVTiA9IGZ1bmN0aW9uKG9uZV9nZW5lKSB7CiAgcCA9IGFxdWFyaXVzOjpwbG90X3NwbGl0X2RpbXJlZChzb2JqX2hmc2MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJzYW1wbGVfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcl9ieSA9IG9uZV9nZW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JfcGFsZXR0ZSA9IGMoImdyYXk3MCIsICIjRkRCQjg0IiwgIiNFRjY1NDgiLCAiIzdGMDAwMCIsICJibGFjayIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbl9wdF9zaXplID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfcHRfc2l6ZSA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX2NvbG9yID0gImdyYXk5NSIpCiAgcCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwLCBucm93ID0gMSkgKwogICAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpICsKICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICYKICAgIGdncGxvdDI6OnRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZmVhdHVyZXNfb2kKcGxvdF9saXN0CmBgYAoKVmlvbGluIHBsb3RzIHNwbGl0IGJ5IHNhbXBsZSB0eXBlIDoKCmBgYHtyIGZpZ19oZnNjX3Zsbl9zYW1wbGVfdHlwZSwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDMsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXNfb2ksIEZVTiA9IGZ1bmN0aW9uKGZlYXR1cmUpIHsKICAjIHQtdGVzdCBiZXR3ZWVuIHNhbXBsZSB0eXBlCiAgZmVhdHVyZV9leHByID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbZmVhdHVyZSwgXQogIGZlYXR1cmVfaHMgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQogIGZlYXR1cmVfaGQgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCA9IHN0YXRzOjp0LnRlc3QoZmVhdHVyZV9ocywgZmVhdHVyZV9oZCkKICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQKICAKICAjIFNpZ25pZmljYW5jZSBhc3NvY2lhdGVkIHdpdGggcC12YWx1ZQogIHB2YWwgPSBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQkcC52YWx1ZQogIAogIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbihwdmFsID4gMC4wNSB+ICJucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAxICYgcHZhbCA8PSAwLjA1IH4gIioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMDEgJiBwdmFsIDw9IDAuMDEgfiAiKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsIDw9IDAuMDAxIH4gIioqKiIpCiAgCiAgIyBQbG90CiAgcCA9IFNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmUpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV90eXBlX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICAgICMgU2lnbmlmaWNhbmNlIGJhcgogICAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEsIHhlbmQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjMsIHllbmQgPSBtYXgoZmVhdHVyZV9leHByKSswLjMpICsKICAgIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSAxLjUsIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjM1LAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNpZ25pZmljYW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBsYWJlbC5zaXplID0gMCkgKwogICAgZ2dwbG90Mjo6bGltcyh5ID0gYygwLCBtYXgoZmVhdHVyZV9leHByKSswLjQpKSArCiAgICAjIFRoZW1lCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZmVhdHVyZXNfb2kKcGxvdF9saXN0CmBgYAoKCkJhcnBsb3Qgd2l0aCBudW1iZXIgb2YgSEZTQ3MgYW5kIHRvdGFsIG51bWJlciBvZiBjZWxscyA6CgpgYGB7ciBmaWdfaGZzY19iYXJwbG90LCBmaWcud2lkdGggPSA1LjUsIGZpZy5oZWlnaHQgPSA0LjV9CnF1YW50aWYgPSBkcGx5cjo6bGVmdF9qb2luKAogIHggPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICAgIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAibmJfY2VsbHMiKSksCiAgeSA9IHRhYmxlKHNvYmpfaGZzYyRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2hmc2MiKSksCiAgYnkgPSAiU2FtcGxlIikgJT4lCiAgZHBseXI6Om11dGF0ZShwcm9wX2hmc2MgPSByb3VuZCgxMDAqbmJfaGZzYyAvIG5iX2NlbGxzLCAyKSkKCnF1YW50aWZfdG9fcGxvdCA9IHJiaW5kLmRhdGEuZnJhbWUoCiAgZGF0YS5mcmFtZShTYW1wbGUgPSBxdWFudGlmJFNhbXBsZSwKICAgICAgICAgICAgIG5iX2NlbGxzID0gcXVhbnRpZiRuYl9jZWxscyAtIHF1YW50aWYkbmJfaGZzYywKICAgICAgICAgICAgIGNlbGxfdHlwZSA9ICJvdGhlcnMiLAogICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSwKICBkYXRhLmZyYW1lKFNhbXBsZSA9IHF1YW50aWYkU2FtcGxlLAogICAgICAgICAgICAgbmJfY2VsbHMgPSBxdWFudGlmJG5iX2hmc2MsCiAgICAgICAgICAgICBjZWxsX3R5cGUgPSAiaGZzYyIsCiAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNlbGxfdHlwZSA9IGZhY3RvcihjZWxsX3R5cGUsIGxldmVscyA9IGMoIm90aGVycyIsICJoZnNjIikpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHF1YW50aWZfdG9fcGxvdCwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAibmJfY2VsbHMiLCBmaWxsID0gImNlbGxfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsKICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBxdWFudGlmLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSAuZGF0YSRTYW1wbGUsIHkgPSAwLjA1Ky5kYXRhJHByb3BfaGZzYy8xMDAsIGxhYmVsID0gLmRhdGEkbmJfaGZzYyksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDUsIGZpbGwgPSBOQSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXk5MCIsIGNvbG9yX21hcmtlcnNbWyJIRlNDIl1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJvdGhlcnMiLCAiaGZzYyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2VsbCBUeXBlIikgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKHNlcSgwLCAxMDAsIGJ5ID0gMjUpLCBzZXAgPSAiICUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZ2dwbG90Mjo6ZXhwYW5zaW9uKGFkZCA9IGMoMCwgMC4wNSkpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJsaWdodGdyYXkiKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMgSUJMIGFuZCBPUlMKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBJQkwgKyBPUlMgZGF0YXNldCA6CgpgYGB7ciBzb2JqX2libG9yc30Kc29ial9pYmxvcnMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vM196b29tX2libG1vcnMvaWJsbW9yc19zb2JqLnJkcyIpKQpzb2JqX2libG9ycwpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX2libG9yc30KbmFtZTJEID0gImhhcm1vbnlfMjBfdHNuZSIKYGBgCgpUbyByZXByZXNlbnQgcmVzdWx0cyBmcm9tIGRpZmZlcmVudGlhbCBleHByZXNzaW9uLCB3ZSBsb2FkIHRoZSBhbmFseXNlcyByZXN1bHRzIDoKCmBgYHtyIGxpc3RfcmVzdWx0c19pYmxvcnN9Cmxpc3RfcmVzdWx0cyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS8zX3pvb21faWJsbW9ycy9pYmxtb3JzX2xpc3RfcmVzdWx0cy5yZHMiKSkKCmxhcHBseShsaXN0X3Jlc3VsdHMsIEZVTiA9IG5hbWVzKQpgYGAKCiMjIFByZXBhcmF0aW9uCgpXZSBkZWZpbmVkIGNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfaWJsb3JzLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0KY2x1c3Rlcl90eXBlID0gdGFibGUoc29ial9pYmxvcnMkY2VsbF90eXBlLCBzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqX2libG9ycyRjZWxsX3R5cGUpW2NsdXN0ZXJfdHlwZV0pCgpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBjbHVzdGVyX3R5cGVbc29ial9pYmxvcnMkc2V1cmF0X2NsdXN0ZXJzXQpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPSBmYWN0b3Ioc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiSUJMIiwgIk9SUyIpKQpgYGAKCiMjIEZpZ3VyZXMKCklCTCArIE9SUyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdfaWJsb3JzX2xvY2F0aW9uLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0Kc29iaiRjZWxsX2JjID0gY29sbmFtZXMoc29iaikKc29ial9pYmxvcnMkY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmpfaWJsb3JzKQpzb2JqJGlzX2libG9ycyA9IGRwbHlyOjpsZWZ0X2pvaW4oc29iakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2libG9yc0BtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNsdXN0ZXJfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gImNlbGxfYmMiKVssICJjbHVzdGVyX3R5cGUiXQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfaWJsb3JzIiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjb2xvcl9tYXJrZXJzW2MoIklCTCIsICJPUlMiKV0sIGJnX2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSUJMIiwgIk9SUyIsIE5BKSwgbmEudmFsdWUgPSBiZ19jb2xvcikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiSUJMICsgT1JTIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9pYmxvcnMpLCAiIGNlbGxzIikpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCgpQcm9qZWN0IG5hbWUgOgoKYGBge3IgZmlnX2libG9yc19wcm9qZWN0X25hbWUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29ial9pYmxvcnMpLCByZXBsYWNlID0gRkFMU0UsIHNpemUgPSBuY29sKHNvYmpfaWJsb3JzKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpfaWJsb3JzQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRwcm9qZWN0X25hbWUgPSBzb2JqX2libG9ycyRwcm9qZWN0X25hbWUKY2VsbHNfY29vcmQgPSBjZWxsc19jb29yZFsocm5kX29yZGVyKSwgXQoKIyBQbG90CmdncGxvdDI6OmdncGxvdChjZWxsc19jb29yZCwgYWVzKHggPSBEaW0xLCB5ID0gRGltMiwgY29sID0gcHJvamVjdF9uYW1lKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDEuMikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKSArCiAgZ2dwbG90Mjo6dGhlbWVfdm9pZCgpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpDbHVzdGVyIDoKCmBgYHtyIGZpZ19pYmxvcnNfY2x1c3RlcnMsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLCBjb2xzID0gZ3JleV9wYWxldHRlLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLCBsYWJlbC5zaXplID0gOCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgOgoKYGBge3IgZmlnX2libG9yc19jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpDbHVzdGVyIHR5cGUgc3BsaXQgYnkgc2FtcGxlIHR5cGUgOgoKYGBge3IgZmlnX2libG9yc19jbHVzdGVyX3R5cGVfc3BsaXQsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA0fQpwbG90X2xpc3QgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9jb2xvciA9IGNvbG9yX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcGxpdF9ieSA9ICJzYW1wbGVfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZ19wdF9zaXplID0gMSwgbWFpbl9wdF9zaXplID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJnX2NvbG9yID0gYmdfY29sb3IpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0KSAmCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKTnVtYmVyIG9mIGNlbGxzIGJ5IGNsdXN0ZXIgYW5kIGJ5IHNhbXBsZSA6CgpgYGB7ciBpYmxfb3JzX3RhYmxlX2NsdXN0ZXJzfQp0YWJsZShzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnMsCiAgICAgIHNvYmpfaWJsb3JzJHNhbXBsZV9pZGVudGlmaWVyKQoKdGFibGUoc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlLAogICAgICBzb2JqX2libG9ycyRzYW1wbGVfaWRlbnRpZmllcikKYGBgCgpTZXBhcmF0ZSBjbHVzdGVyIDUgOgoKYGBge3IgaWJsX29yc190YWJsZV9jbHVzdGVyc19zZXA1fQpzb2JqX2libG9ycyRjbHVzdGVyX3R5cGVfc2VwNSA9IGlmZWxzZShzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnMgPT0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gIk9SU181IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBhcy5jaGFyYWN0ZXIoc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlKSkgJT4lCiAgYXMuZmFjdG9yKCkKCnRhYmxlKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZV9zZXA1LAogICAgICBzb2JqX2libG9ycyRzYW1wbGVfdHlwZSkKYGBgCgoKQmFycGxvdCBieSBjbHVzdGVyIGNhdGVnb3J5IDoKCmBgYHtyIGZpZ19pYmxvcnNfYmFycGxvdCwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNC41fQpxdWFudGlmID0gdGFibGUoc29ial9pYmxvcnMkc2FtcGxlX2lkZW50aWZpZXIpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIm5iX2NlbGxzIikpCgpxdWFudGlmX3RvX3Bsb3QgPSB0YWJsZShzb2JqX2libG9ycyRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlX3NlcDUpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGxUeXBlIiwgIk51bWJlciIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKFN0eWxlID0gaWZlbHNlKENlbGxUeXBlID09ICJPUlNfNSIsIHllcyA9ICJJTDFSMisiLCBubyA9ICJJTDFSMi0iKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTdHlsZSA9IGZhY3RvcihTdHlsZSwgbGV2ZWxzID0gYygiSUwxUjItIiwgIklMMVIyKyIpKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDZWxsVHlwZSA9IGlmZWxzZShDZWxsVHlwZSA9PSAiT1JTXzUiLCB5ZXMgPSAiT1JTIiwgbm8gPSBhcy5jaGFyYWN0ZXIoQ2VsbFR5cGUpKSkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJDZWxsIFR5cGUiLCAiTnVtYmVyIiwgIklMMVIyIHN0YXR1cyIpKQoKYXF1YXJpdXM6OnBsb3RfYmFycGxvdChkZiA9IHF1YW50aWZfdG9fcGxvdCwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gIlNhbXBsZSIsIHkgPSAiTnVtYmVyIiwKICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gIkNlbGwgVHlwZSIsIHBhdHRlcm4gPSAiSUwxUjIgc3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwoKSkgKwogIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IHF1YW50aWYsIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IC5kYXRhJFNhbXBsZSwgeSA9IDEuMDUsIGxhYmVsID0gLmRhdGEkbmJfY2VsbHMpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDAsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdW5saXN0KGNvbG9yX21hcmtlcnNbbGV2ZWxzKHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSldKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjb2xvcl9tYXJrZXJzW2xldmVscyhzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUpXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJDZWxsIFR5cGUiKSArCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgYnkgPSAyNSksIHNlcCA9ICIgJSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBnZ3Bsb3QyOjpleHBhbnNpb24oYWRkID0gYygwLCAwLjA1KSkpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImxpZ2h0Z3JheSIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQpgYGAKCkZvY3VzIG9uIGNsdXN0ZXIgNSwgYW1vbmcgT1JTIDoKCmBgYHtyIGZpZ19pYmxvcnNfYmFycGxvdF9jbHVzdGVyNSwgZmlnLndpZHRoID0gNS41LCBmaWcuaGVpZ2h0ID0gNC41fQpzdWJzb2JqID0gc3Vic2V0KHNvYmpfaWJsb3JzLCBjbHVzdGVyX3R5cGUgPT0gIk9SUyIpCnN1YnNvYmokY2x1c3Rlcl90eXBlX3NlcDUgPSBpZmVsc2Uoc3Vic29iaiRzZXVyYXRfY2x1c3RlcnMgPT0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiT1JTXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gYXMuY2hhcmFjdGVyKHN1YnNvYmokY2x1c3Rlcl90eXBlKSkgJT4lCiAgYXMuZmFjdG9yKCkKCnF1YW50aWYgPSB0YWJsZShzdWJzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKcXVhbnRpZl90b19wbG90ID0gdGFibGUoc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgc3Vic29iaiRjbHVzdGVyX3R5cGVfc2VwNSkgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbFR5cGUiLCAiTnVtYmVyIikpICU+JQogIGRwbHlyOjptdXRhdGUoU3R5bGUgPSBpZmVsc2UoQ2VsbFR5cGUgPT0gIk9SU181IiwgeWVzID0gIklMMVIyKyIsIG5vID0gIklMMVIyLSIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKFN0eWxlID0gZmFjdG9yKFN0eWxlLCBsZXZlbHMgPSBjKCJJTDFSMi0iLCAiSUwxUjIrIikpKSAlPiUKICBkcGx5cjo6bXV0YXRlKENlbGxUeXBlID0gaWZlbHNlKENlbGxUeXBlID09ICJPUlNfNSIsIHllcyA9ICJPUlMiLCBubyA9IGFzLmNoYXJhY3RlcihDZWxsVHlwZSkpKSAlPiUKICBgY29sbmFtZXM8LWAoYygiU2FtcGxlIiwgIkNlbGwgVHlwZSIsICJOdW1iZXIiLCAiSUwxUjIgc3RhdHVzIikpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4oLiwgeSA9IHF1YW50aWYsIGJ5ID0gIlNhbXBsZSIpICU+JQogIGRwbHlyOjptdXRhdGUocHJvcF9jbHVzdGVyNSA9IHJvdW5kKE51bWJlci9uYl9jZWxscywgNCkpCgphcXVhcml1czo6cGxvdF9iYXJwbG90KGRmID0gcXVhbnRpZl90b19wbG90LAogICAgICAgICAgICAgICAgICAgICAgIHggPSAiU2FtcGxlIiwgeSA9ICJOdW1iZXIiLAogICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSAiSUwxUjIgc3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwoKSkgKwogICMgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZiwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAjICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSAuZGF0YSRTYW1wbGUsIHkgPSAxLjA1LCBsYWJlbCA9IC5kYXRhJG5iX2NlbGxzKSwKICAjICAgICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IDAsIHNpemUgPSA1KSArCiAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gcXVhbnRpZl90b19wbG90ICU+JQogICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKGBJTDFSMiBzdGF0dXNgID09ICJJTDFSMisiKSwKICAgICAgICAgICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IC5kYXRhJFNhbXBsZSwgeSA9IHByb3BfY2x1c3RlcjUgKyAwLjA1LCBsYWJlbCA9IC5kYXRhJE51bWJlciksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMCwgc2l6ZSA9IDUsIGZpbGwgPSBOQSwgY29sID0gIndoaXRlIikgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgY29sb3JfbWFya2Vyc1tbIk9SUyJdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSUwxUjIrIiwgIklMMVIyLSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSUwxUjIgc3RhdHVzIikgKwogIGdncGxvdDI6OnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gcGFzdGUwKHNlcSgwLCAxMDAsIGJ5ID0gMjUpLCBzZXAgPSAiICUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZ2dwbG90Mjo6ZXhwYW5zaW9uKGFkZCA9IGMoMCwgMC4wNSkpKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJsaWdodGdyYXkiKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgoKREUgZ2VuZXMgYmV0d2VlbiBJQkwgYW5kIE9SUyA6CgpgYGB7ciBmaWdfaWJsb3JzX2RlX3BvcCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cm1hcmsgPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRtYXJrCm1hcmskZ2VuZV9uYW1lID0gcm93bmFtZXMobWFyaykKbWFya19sYWJlbCA9IHJiaW5kKAogICMgdXAtcmVndWxhdGVkIGluIElCTAogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gYXZnX2xvZ0ZDKSwKICAjIHVwLXJlZ3VsYXRlZCBpbiBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC1hdmdfbG9nRkMpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgSUJMCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAocGN0LjEgLSBwY3QuMikpLAogICMgcmVwcmVzZW50YXRpdmUgYW5kIHNlbGVjdGl2ZSBmb3IgT1JTCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSAtKHBjdC4xIC0gcGN0LjIpKSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkKbWFya19sYWJlbCA9IG1hcmtfbGFiZWxbIWdyZXBsKHJvd25hbWVzKG1hcmtfbGFiZWwpLCBwYXR0ZXJuID0gIl5NVCIpLCBdCgphdmdfbG9nRkNfcmFuZ2UgPSBzZXROYW1lcyhjKG1pbihtYXJrX2xhYmVsJGF2Z19sb2dGQyksIC0xLCAwLCAxLCBtYXgobWFya19sYWJlbCRhdmdfbG9nRkMpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBjKCJkb2RnZXJibHVlNCIsICJkb2RnZXJibHVlMyIsICIjQjdCN0I3IiwgImZpcmVicmljazMiLCAiZmlyZWJyaWNrNCIpKQoKCmdncGxvdDI6OmdncGxvdChtYXJrLCBhZXMoeCA9IHBjdC4xLCB5ID0gcGN0LjIsIGNvbCA9IGF2Z19sb2dGQykpICsKICBnZ3Bsb3QyOjpnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KCkgKwogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YSA9IG1hcmtfbGFiZWwsIG1heC5vdmVybGFwcyA9IEluZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgbGFiZWwgPSBnZW5lX25hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMy41LCBsYWJlbC5zaXplID0gTkEpICsKICBnZ3Bsb3QyOjpsYWJzKHggPSAiRW5yaWNoZWQgaW4gSUJMIiwKICAgICAgICAgICAgICAgIHkgPSAiRW5yaWNoZWQgaW4gT1JTIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBuYW1lcyhhdmdfbG9nRkNfcmFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBzY2FsZXM6OnJlc2NhbGUodW5uYW1lKGF2Z19sb2dGQ19yYW5nZSkpKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKCkdTRUEgcGxvdCA6CgpgYGB7ciBmaWdfaWJsb3JzX2tlcmF0aW5pemF0aW9uLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiUkVBQ1RPTUVfS0VSQVRJTklaQVRJT04iIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWFAcmVzdWx0ICU+JQogIGRwbHlyOjpmaWx0ZXIoSUQgPT0gdGhlX2dzX25hbWUpCnRoZV9zdWJ0aXRsZSA9IHBhc3RlMCgiXG5ORVMgOiAiLCByb3VuZCh0aGVfY29udGVudCRORVMsIDIpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0cHZhbHVlIDogIiwgcm91bmQodGhlX2NvbnRlbnQkcHZhbHVlLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHNldCBzaXplIDogIiwgdGhlX2NvbnRlbnQkc2V0U2l6ZSwgIiBnZW5lcyIpCgplbnJpY2hwbG90Ojpnc2VhcGxvdDIoeCA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKYGBge3IgZmlnX2libG9yc19pZm5hLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiSEFMTE1BUktfSU5URVJGRVJPTl9HQU1NQV9SRVNQT05TRSIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRwdmFsdWUgOiAiLCByb3VuZCh0aGVfY29udGVudCRwdmFsdWUsIDQpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0c2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpTY29yZSBmb3IgYm90aCBnZW5lIHNldHMsIGluIGFsbCBjZWxscyA6CgpgYGB7ciBwbG90X3Zsbl9zY29yZSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnNpZ25pZmljYW50ID0gZnVuY3Rpb24ocHZhbCkgewogIHNpZ25pZiA9IGNhc2Vfd2hlbihwdmFsID4gMC4wNSB+ICJucyIsCiAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAxICYgcHZhbCA8PSAwLjA1IH4gIioiLAogICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMDEgJiBwdmFsIDw9IDAuMDEgfiAiKioiLAogICAgICAgICAgICAgICAgICAgICBwdmFsIDw9IDAuMDAxIH4gIioqKiIpCiAgcmV0dXJuKHNpZ25pZikKfQoKcGxvdF92bG5fc2NvcmUgPSBmdW5jdGlvbihzY29yZV9uYW1lLCB0aGVfZ3NfbmFtZSkgewogICMgdC10ZXN0IGJldHdlZW4gc2FtcGxlIHR5cGUKICBmZWF0dXJlX2V4cHIgPSBzb2JqX2libG9yc0BtZXRhLmRhdGFbLCBzY29yZV9uYW1lXQogIAogICMjIGluIG1PUlMKICBmZWF0dXJlX2hzID0gZmVhdHVyZV9leHByW3NvYmpfaWJsb3JzJHNhbXBsZV90eXBlID09ICJIUyIgJiBzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUgPT0gIk9SUyJdCiAgZmVhdHVyZV9oZCA9IGZlYXR1cmVfZXhwcltzb2JqX2libG9ycyRzYW1wbGVfdHlwZSA9PSAiSEQiICYgc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID09ICJPUlMiXQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCA9IHN0YXRzOjp0LnRlc3QoZmVhdHVyZV9ocywgZmVhdHVyZV9oZCkKICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQKICBwdmFsX29ycyA9IGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCRwLnZhbHVlCiAgCiAgIyMgaW4gSUJMCiAgZmVhdHVyZV9ocyA9IGZlYXR1cmVfZXhwcltzb2JqX2libG9ycyRzYW1wbGVfdHlwZSA9PSAiSFMiICYgc29ial9pYmxvcnMkY2x1c3Rlcl90eXBlID09ICJJQkwiXQogIGZlYXR1cmVfaGQgPSBmZWF0dXJlX2V4cHJbc29ial9pYmxvcnMkc2FtcGxlX3R5cGUgPT0gIkhEIiAmIHNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSA9PSAiSUJMIl0KICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQgPSBzdGF0czo6dC50ZXN0KGZlYXR1cmVfaHMsIGZlYXR1cmVfaGQpCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkCiAgcHZhbF9pYmwgPSBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQkcC52YWx1ZQogIAogICMgU2lnbmlmaWNhbmNlIGFzc29jaWF0ZWQgd2l0aCBwLXZhbHVlCiAgc2lnbmlmaWNhbmNlX29ycyA9IHNpZ25pZmljYW50KHB2YWxfb3JzKQogIHNpZ25pZmljYW5jZV9pYmwgPSBzaWduaWZpY2FudChwdmFsX2libCkKICAKICAjIFBsb3QKICBwID0gU2V1cmF0OjpWbG5QbG90KHNvYmpfaWJsb3JzLCBmZWF0dXJlcyA9IHNjb3JlX25hbWUsIHB0LnNpemUgPSAwLjA1LAogICAgICAgICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAic2FtcGxlX3R5cGUiLCBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgICAjIFNpZ25pZmljYW5jZSBiYXIgKElCTCkKICAgIGdncGxvdDI6Omdlb21fc2VnbWVudChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgIHggPSAwLjc1LCB4ZW5kID0gMS4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWF4KGZlYXR1cmVfZXhwcikqMS4yLCB5ZW5kID0gbWF4KGZlYXR1cmVfZXhwcikqMS4yKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2xhYmVsKGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICB4ID0gMSwgeSA9IG1heChmZWF0dXJlX2V4cHIpKjEuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gc2lnbmlmaWNhbmNlX2libCwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBsYWJlbC5zaXplID0gMCkgKwogICAgIyBTaWduaWZpY2FuY2UgYmFyIChtT1JTKQogICAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEuNzUsIHhlbmQgPSAyLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtYXgoZmVhdHVyZV9leHByKSoxLjIsIHllbmQgPSBtYXgoZmVhdHVyZV9leHByKSoxLjIpICsKICAgIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSAyLCB5ID0gbWF4KGZlYXR1cmVfZXhwcikqMS4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzaWduaWZpY2FuY2Vfb3JzLAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGxhYmVsLnNpemUgPSAwKSArCiAgICBnZ3Bsb3QyOjpsaW1zKHkgPSBjKDAsIDEuMyptYXgoZmVhdHVyZV9leHByKSkpICsKICAgICMgU3R5bGUKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSkgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogIAogIHJldHVybihwKQp9CmBgYAoKYGBge3IgZmlnX2libG9yc19rZXJhX3Njb3JlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KZ2VuZV9zZXRzID0gYXF1YXJpdXM6OmdldF9nZW5lX3NldHMoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKQoKIyBHZW5lcyBpbiB0aGUgZ2VuZSBzZXQgb2YgaW50ZXJlc3QKdGhlX2dzX25hbWUgPSAiUkVBQ1RPTUVfS0VSQVRJTklaQVRJT04iCnRoZV9nc19jb250ZW50ID0gZ2VuZV9zZXRzJGdlbmVfc2V0c19mdWxsICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSA9PSB0aGVfZ3NfbmFtZSkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9zeW1ib2wpICU+JQogIHVubGlzdCgpCgojIEF0dHJpYnV0ZSBhIHNjb3JlCnNvYmpfaWJsb3JzJHNjb3JlX2tlcmEgPSBTZXVyYXQ6OkFkZE1vZHVsZVNjb3JlKHNvYmpfaWJsb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QodGhlX2dzX2NvbnRlbnQpKSRDbHVzdGVyMQoKIyBWaW9saW4gcGxvdApwbG90X3Zsbl9zY29yZSgic2NvcmVfa2VyYSIsIHRoZV9nc19uYW1lKQpgYGAKCmBgYHtyIGZpZ19pYmxvcnNfaWZuYV9zY29yZSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CiMgR2VuZXMgaW4gdGhlIGdlbmUgc2V0IG9mIGludGVyZXN0CnRoZV9nc19uYW1lID0gIkhBTExNQVJLX0lOVEVSRkVST05fR0FNTUFfUkVTUE9OU0UiCnRoZV9nc19jb250ZW50ID0gZ2VuZV9zZXRzJGdlbmVfc2V0c19mdWxsICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSA9PSB0aGVfZ3NfbmFtZSkgJT4lCiAgZHBseXI6OnB1bGwoZ2VuZV9zeW1ib2wpICU+JQogIHVubGlzdCgpCgojIEF0dHJpYnV0ZSBhIHNjb3JlCnNvYmpfaWJsb3JzJHNjb3JlX2lmbmcgPSBTZXVyYXQ6OkFkZE1vZHVsZVNjb3JlKHNvYmpfaWJsb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGxpc3QodGhlX2dzX2NvbnRlbnQpKSRDbHVzdGVyMQoKIyBWaW9saW4gcGxvdApwbG90X3Zsbl9zY29yZSgic2NvcmVfaWZuZyIsIHRoZV9nc19uYW1lKQpgYGAKCgpWaW9saW4gcGxvdCBmb3IgSUJMIDoKCmBgYHtyIGZpZ19pYmxvcnNfaWJsLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiSUJMIikKZmVhdHVyZXNfb2kgPSBjKCJEVVNQMSIsICJERElUNCIsICJNSUYiLCAiTEdBTFM3IiwgIkFSRjUiLCAiUzEwMEE5IikKCiMgUGxvdCAhCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24oZmVhdHVyZSkgewogICMgdC10ZXN0IGJldHdlZW4gc2FtcGxlIHR5cGUKICBmZWF0dXJlX2V4cHIgPSBzdWJzb2JqQGFzc2F5cyRSTkFAZGF0YVtmZWF0dXJlLCBdCiAgZmVhdHVyZV9ocyA9IGZlYXR1cmVfZXhwcltzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIUyJdCiAgZmVhdHVyZV9oZCA9IGZlYXR1cmVfZXhwcltzdWJzb2JqJHNhbXBsZV90eXBlID09ICJIRCJdCiAgZmVhdHVyZV9oc19WU19mZWF0dXJlX2hkID0gc3RhdHM6OnQudGVzdChmZWF0dXJlX2hzLCBmZWF0dXJlX2hkKQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZAogIAogICMgU2lnbmlmaWNhbmNlIGFzc29jaWF0ZWQgd2l0aCBwLXZhbHVlCiAgcHZhbCA9IGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCRwLnZhbHVlCiAgCiAgc2lnbmlmaWNhbmNlID0gY2FzZV93aGVuKHB2YWwgPiAwLjA1IH4gIm5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbCA+IDAuMDEgJiBwdmFsIDw9IDAuMDUgfiAiKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAwMSAmIHB2YWwgPD0gMC4wMSB+ICIqKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPD0gMC4wMDEgfiAiKioqIikKICAKICAjIFBsb3QKICBwID0gU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV90eXBlIiwgcHQuc2l6ZSA9IDAuMywKICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gZmVhdHVyZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc2FtcGxlX3R5cGVfY29sb3JzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gbmFtZXMoc2FtcGxlX3R5cGVfY29sb3JzKSkgKwogICAgIyBTaWduaWZpY2FuY2UgYmFyCiAgICBnZ3Bsb3QyOjpnZW9tX3NlZ21lbnQoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gMSwgeGVuZCA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMywgeWVuZCA9IG1heChmZWF0dXJlX2V4cHIpKzAuMykgKwogICAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gZGF0YS5mcmFtZSgxKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEuNSwgeSA9IG1heChmZWF0dXJlX2V4cHIpKzAuMzUsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gc2lnbmlmaWNhbmNlLAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGxhYmVsLnNpemUgPSAwKSArCiAgICBnZ3Bsb3QyOjpsaW1zKHkgPSBjKDAsIG1heChmZWF0dXJlX2V4cHIpKzAuNCkpICsKICAgICMgVGhlbWUKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICAKICByZXR1cm4ocCkKfSkKCm5hbWVzKHBsb3RfbGlzdCkgPSBmZWF0dXJlc19vaQpwbG90X2xpc3QKYGBgCgpWaW9saW4gcGxvdCBmb3IgT1JTIDoKCmBgYHtyIGZpZ19pYmxvcnNfb3JzLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMywgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiT1JTIikKZmVhdHVyZXNfb2kgPSBjKCJEVVNQMSIsICJLTEY2IiwgIkNMRE4xIiwgIkNUR0YiLAogICAgICAgICAgICAgICAgIlMxMDBBOSIsICJDQ0wyIiwgIklGSVRNMyIsICJJRkkyNyIpCgojIFBsb3QgIQpwbG90X2xpc3QgPSBsYXBwbHkoZmVhdHVyZXNfb2ksIEZVTiA9IGZ1bmN0aW9uKGZlYXR1cmUpIHsKICAjIHQtdGVzdCBiZXR3ZWVuIHNhbXBsZSB0eXBlCiAgZmVhdHVyZV9leHByID0gc3Vic29iakBhc3NheXMkUk5BQGRhdGFbZmVhdHVyZSwgXQogIGZlYXR1cmVfaHMgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSFMiXQogIGZlYXR1cmVfaGQgPSBmZWF0dXJlX2V4cHJbc3Vic29iaiRzYW1wbGVfdHlwZSA9PSAiSEQiXQogIGZlYXR1cmVfaHNfVlNfZmVhdHVyZV9oZCA9IHN0YXRzOjp0LnRlc3QoZmVhdHVyZV9ocywgZmVhdHVyZV9oZCkKICBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQKICAKICAjIFNpZ25pZmljYW5jZSBhc3NvY2lhdGVkIHdpdGggcC12YWx1ZQogIHB2YWwgPSBmZWF0dXJlX2hzX1ZTX2ZlYXR1cmVfaGQkcC52YWx1ZQogIAogIHNpZ25pZmljYW5jZSA9IGNhc2Vfd2hlbihwdmFsID4gMC4wNSB+ICJucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPiAwLjAxICYgcHZhbCA8PSAwLjA1IH4gIioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsID4gMC4wMDEgJiBwdmFsIDw9IDAuMDEgfiAiKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsIDw9IDAuMDAxIH4gIioqKiIpCiAgCiAgIyBQbG90CiAgcCA9IFNldXJhdDo6VmxuUGxvdChzdWJzb2JqLCBncm91cC5ieSA9ICJzYW1wbGVfdHlwZSIsIHB0LnNpemUgPSAwLjMsCiAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGZlYXR1cmUpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHNhbXBsZV90eXBlX2NvbG9ycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICAgICMgU2lnbmlmaWNhbmNlIGJhcgogICAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KGRhdGEgPSBkYXRhLmZyYW1lKDEpLCBpbmhlcml0LmFlcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDEsIHhlbmQgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjMsIHllbmQgPSBtYXgoZmVhdHVyZV9leHByKSswLjMpICsKICAgIGdncGxvdDI6Omdlb21fbGFiZWwoZGF0YSA9IGRhdGEuZnJhbWUoMSksIGluaGVyaXQuYWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSAxLjUsIHkgPSBtYXgoZmVhdHVyZV9leHByKSswLjM1LAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHNpZ25pZmljYW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLCBsYWJlbC5zaXplID0gMCkgKwogICAgZ2dwbG90Mjo6bGltcyh5ID0gYygwLCBtYXgoZmVhdHVyZV9leHByKSswLjQpKSArCiAgICAjIFRoZW1lCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZmVhdHVyZXNfb2kKcGxvdF9saXN0CmBgYAoKU3BsaXQgYnkgc2FtcGxlIDoKCmBgYHtyIGZpZ19pYmxvcnNfb3JzX3NwbGl0LCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpWbG5QbG90KHN1YnNvYmosIGdyb3VwLmJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gIklGSTI3IiwgY29scyA9IHNhbXBsZV9pbmZvJGNvbG9yKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpIZWF0bWFwIGZvciBjbHVzdGVyIDUgdnMgb3RoZXIgT1JTIDoKCmBgYHtyIGZpZ19pYmxvcnNfaGVhdG1hcF9jbHVzdGVyNSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxOSwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnN1YnNvYmogPSBzdWJzZXQoc29ial9pYmxvcnMsIGNsdXN0ZXJfdHlwZSA9PSAiT1JTIikKCmZlYXR1cmVzX29pID0gYygiWUJYMyIsICJUWE5JUCIsICJLUlQxNCIsICJLUlQxNSIsICJORUFUMSIsCiAgICAgICAgICAgICAgICAiRlhZRDMiLCAiTVQyQSIsICJNVDFFIiwgIk1UMVgiLCAiQVFQMyIsICJHTFVMIiwKICAgICAgICAgICAgICAgICMgIkhBTExNQVJLX1RORkFfU0lHTkFMSU5HX1ZJQV9ORktCIgogICAgICAgICAgICAgICAgIkZPUyIsICJKVU5CIiwgIkRVU1AxIiwgIlpGUDM2IiwgIk5GS0JJWiIsCiAgICAgICAgICAgICAgICAiQVRGMyIsICJSSE9CIiwgICJFVFMyIiwgIklMMTgiLCAiS0xGNCIsICJLTEY2IiwgIktMRjkiLAogICAgICAgICAgICAgICAgIktMRjMiLCAiS0xGNSIsICJDT0wxN0ExIiwgIlRIU0Q0IiwgIldOVDMiLCAiV05UNCIsICJTTFBJIiwgIlBMQVQiLAogICAgICAgICAgICAgICAgIkxBTUI0IiwgIkRDTiIsICJTUElOSzUiLAogICAgICAgICAgICAgICAgIkdTVE0zIiwgIkFMREgzQTEiLCAgIkxHQUxTN0IiLCAiU0xDMzhBMiIsICJFSEYiLCAgIkNMRUMyQiIsCiAgICAgICAgICAgICAgICAiSUwyMFJCIiwgIklMMVIyIiwgIklGSTI3IiwgIkNYQ0wxNCIsICJITEEtQyIsICJHUFNNMiIsICJEQUFNMSIsICAgIklEMSIsCiAgICAgICAgICAgICAgICAiUk5BU0VUMiIsICJIT1BYIiwgIlBPVTNGMSIsICJTUFJZMSIsICJBUiIsICJQREdGQyIsCiAgICAgICAgICAgICAgICAiV0ZEQzIiLCAiV0ZEQzUiLCAiVFNDMjJEMyIsICJGR0ZSMyIsICAiTFk2RCIsICJJR0ZCUDMiLCAKICAgICAgICAgICAgICAgICMgT3RoZXIgT1JTCiAgICAgICAgICAgICAgICAiQVBPRSIsICJDVFNCIiwgIkNBTEQxIiwgIlNPWDQiLAogICAgICAgICAgICAgICAgIlNUTU4xIiwgIkxNTzQiLCAiQ0VCUEIiLCAiVE1FTTQ1QSIsICJHUFgyIiwgIkMxUVRORjEyIiwgIkdKQjYiLAogICAgICAgICAgICAgICAgIktSVDZBIiwgIktSVDE3IiwgIlJCUDEiLCAiQ0FMTUwzIiwgIlBUTiIsICJEQVBLMiIsCiAgICAgICAgICAgICAgICAiRUdMTjMiLCAiRklMSVAxTCIsICJBREdSTDMiLCAiRlNUIiwgIkVGTkIyIiwgIlNFTUE1QSIsCiAgICAgICAgICAgICAgICAiRkdGUjEiLCAiRUdSMiIsICJDTEROMSIsICJERUZCMSIsICJDQVJEMTgiLCAiTUdTVDEiKQoKIyBNYXRyaXgKbWF0X2V4cHIgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzdWJzb2JqKQptYXRfZXhwciA9IG1hdF9leHByW2ZlYXR1cmVzX29pLCBdCm1hdF9leHByID0gTWF0cml4Ojp0KG1hdF9leHByKQptYXRfZXhwciA9IGR5bnV0aWxzOjpzY2FsZV9xdWFudGlsZShtYXRfZXhwcikgIyBiZXR3ZWVuIDAgYW5kIDEKbWF0X2V4cHIgPSBNYXRyaXg6OnQobWF0X2V4cHIpCmRpbShtYXRfZXhwcikgIyBnZW5lcyB4IGNlbGxzCgojIyBDb2xvcnMKbGlzdF9jb2xvcnMgPSBsaXN0KCkKbGlzdF9jb2xvcnNbWyJleHByZXNzaW9uIl1dID0gcmV2KFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIlJkQnUiLCBuID0gOSkpCmxpc3RfY29sb3JzW1sic2FtcGxlX3R5cGUiXV0gPSBzYW1wbGVfdHlwZV9jb2xvcnMKbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSA9IHNldE5hbWVzKG5tID0gc2FtcGxlX2luZm8kc2FtcGxlX2lkZW50aWZpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaW5mbyRjb2xvcikKbGlzdF9jb2xvcnNbWyJwb3B1bGF0aW9uIl1dID0gc2V0TmFtZXMobm0gPSBjKCJJTDFSMisgT1JTIiwgIm90aGVyIE9SUyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibGFjayIsIGNvbG9yX21hcmtlcnNbIk9SUyJdKSkKbGlzdF9jb2xvcnNbWyJuRmVhdHVyZV9STkEiXV0gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihicmVha3MgPSBzZXEoZnJvbSA9IG1pbihzdWJzb2JqJG5GZWF0dXJlX1JOQSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbWF4KHN1YnNvYmokbkZlYXR1cmVfUk5BKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbChuYW1lID0gIkdyZXlzIiwgbiA9IDkpKQoKIyBDZWxscyBvcmRlcgpjb2x1bW5fb3JkZXIgPSBzdWJzb2JqQG1ldGEuZGF0YSAlPiUKICBkcGx5cjo6bXV0YXRlKHNldXJhdF9jbHVzdGVycyA9IGZhY3RvcihzZXVyYXRfY2x1c3RlcnMsIGxldmVscyA9IGMoNSwgMywgMCwgMSwgNykpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShzYW1wbGVfdHlwZSwgc2V1cmF0X2NsdXN0ZXJzLCBzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgcm93bmFtZXMoKQpjb2x1bW5fb3JkZXIgPSBtYXRjaChjb2x1bW5fb3JkZXIsIHJvd25hbWVzKHN1YnNvYmpAbWV0YS5kYXRhKSkKCiMgQW5ub3RhdGlvbgpoYV90b3AgPSBIZWF0bWFwQW5ub3RhdGlvbihzYW1wbGVfdHlwZSA9IHN1YnNvYmokc2FtcGxlX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gc3Vic29iaiRzYW1wbGVfaWRlbnRpZmllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbiA9IGlmZWxzZShzdWJzb2JqJGNsdXN0ZXJfdHlwZV9zZXA1ID09ICJPUlMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJvdGhlciBPUlMiLCBubyA9ICJJTDFSMisgT1JTIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBsaXN0X2NvbG9yc1tbInNhbXBsZV90eXBlIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbXBsZV9pZGVudGlmaWVyID0gbGlzdF9jb2xvcnNbWyJzYW1wbGVfaWRlbnRpZmllciJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gbGlzdF9jb2xvcnNbWyJwb3B1bGF0aW9uIl1dKSkKCmhhX2JvdHRvbSA9IEhlYXRtYXBBbm5vdGF0aW9uKG5GZWF0dXJlX1JOQSA9IHN1YnNvYmokbkZlYXR1cmVfUk5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KG5GZWF0dXJlX1JOQSA9IGxpc3RfY29sb3JzW1sibkZlYXR1cmVfUk5BIl1dKSkKCiMgSGVhdG1hcApodCA9IEhlYXRtYXAoYXMubWF0cml4KG1hdF9leHByKSwKICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCh0aXRsZSA9ICJFeHByZXNzaW9uIiwgYXQgPSBjKDAsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJsb3ciLCAiaGlnaCIpKSwKICAgICAgICAgICAgIGNvbCA9IGxpc3RfY29sb3JzW1siZXhwcmVzc2lvbiJdXSwKICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGFfdG9wLAogICAgICAgICAgICAgYm90dG9tX2Fubm90YXRpb24gPSBoYV9ib3R0b20sCiAgICAgICAgICAgICAjIENlbGwgZ3JvdXBpbmcKICAgICAgICAgICAgIGNvbHVtbl9zcGxpdCA9IHN1YnNvYmokc2FtcGxlX3R5cGUgJT4lIGFzLmNoYXJhY3RlcigpLAogICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICBjb2x1bW5fb3JkZXIgPSBjb2x1bW5fb3JkZXIsCiAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSBOVUxMLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICMgR2VuZXMKICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDE0LCBmb250ZmFjZSA9ICJwbGFpbiIpLAogICAgICAgICAgICAgIyBTdHlsZQogICAgICAgICAgICAgdXNlX3Jhc3RlciA9IEZBTFNFLAogICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICBib3JkZXIgPSBUUlVFKQoKQ29tcGxleEhlYXRtYXA6OmRyYXcoaHQsCiAgICAgICAgICAgICAgICAgICAgIG1lcmdlX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAicmlnaHQiLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gInJpZ2h0IikKYGBgCgpHZW5lcyBvZiBpbnRlcmVzdCA6CgpgYGB7ciBmaWdfaWJsb3JzX2dlbmVzLCBmaWcud2lkdGggPSAyLCBmaWcuaGVpZ2h0ID0gMn0KZ2VuZXMgPSBjKCJLUlQxNiIsICJDT0wxN0ExIiwgIkRTVCIsICJLUlQ2QiIsICJJTDFSMiIsICJXTlQzIiwKICAgICAgICAgICJJRkkyNyIsICJDWENMMTQiLCAiSUdGQlAzIiwgIktSVDE1IiwgIkNEMjAwIiwgIklMMTgiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9pYmxvcnMkbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaWJsb3JzLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuMjUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZ2VuZXMKcGxvdF9saXN0CmBgYAoKCiMgSEZTQ3MgdG8gSUJMIGFuZCBPUlMKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBtZXJnZWQgZGF0YXNldCA6CgpgYGB7ciBzb2JqX3RyYWp9CnNvYmpfdHJhaiA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzRfem9vbS80X3pvb21faGZzY19pYmxtb3JzL2hmc2NfaWJsbW9yc19zb2JqX3RyYWpfdGluZ2EucmRzIikpCnNvYmpfdHJhagpgYGAKCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIHNvYmpfbmFtZTJEX3RyYWp9Cm5hbWUyRCA9ICJoYXJtb255X2RtIgpgYGAKCldlIGxvYWQgdGhlIHRyYWplY3Rvcnkgb2JqZWN0IGZvciB2aXN1YWxpc2F0aW9uIHB1cnBvc2UgOgoKYGBge3IgbG9hZF90cmFqfQpteV90cmFqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNF96b29tLzRfem9vbV9oZnNjX2libG1vcnMvaGZzY19pYmxtb3JzX215X3RyYWpfdGluZ2EucmRzIikpCmNsYXNzKG15X3RyYWopCmBgYAoKCiMjIFByZXBhcmF0aW9uCgpXZSBkZWZpbmVkIGNlbGwgdHlwZSBiYXNlZCBvbiBpbmRpdmlkdWFsIG9iamVjdCA6CgpgYGB7ciBjbHVzdGVyX3R5cGVfdHJhaiwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDV9CnNvYmpfaWJsb3JzJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX2libG9ycykKc29ial90cmFqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqX3RyYWopCnNvYmpfdHJhaiRjbHVzdGVyX3R5cGUgPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpfdHJhakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgInBlcmNlbnQubXQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfaWJsb3JzQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAiY2x1c3Rlcl90eXBlIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJjZWxsX2JjIilbLCAiY2x1c3Rlcl90eXBlIl0gJT4lIGFzLmNoYXJhY3RlcigpCnNvYmpfdHJhaiRjbHVzdGVyX3R5cGUgPSBpZmVsc2UoY29sbmFtZXMoc29ial90cmFqKSAlaW4lIGNvbG5hbWVzKHNvYmpfaGZzYyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gIkhGU0MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gc29ial90cmFqJGNsdXN0ZXJfdHlwZSkgJT4lCiAgYXMuZmFjdG9yKCkKYGBgCgojIyBGaWd1cmVzCgpDZWxscyBvbiB0aGUgZnVsbCBhdGxhcyA6CgpgYGB7ciBmaWdfdHJhal9sb2NhdGlvbiwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CnNvYmokY2VsbF9iYyA9IGNvbG5hbWVzKHNvYmopCnNvYmpfdHJhaiRjZWxsX2JjID0gY29sbmFtZXMoc29ial90cmFqKQpzb2JqJGlzX3RyYWogPSBkcGx5cjo6bGVmdF9qb2luKHNvYmpAbWV0YS5kYXRhWywgYygiY2VsbF9iYyIsICJwZXJjZW50Lm10IildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfdHJhakBtZXRhLmRhdGFbLCBjKCJjZWxsX2JjIiwgImNsdXN0ZXJfdHlwZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJjZWxsX2JjIilbLCAiY2x1c3Rlcl90eXBlIl0KClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkRfYXRsYXMsIHB0LnNpemUgPSAwLjAwMDAwMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImlzX3RyYWoiLCBvcmRlciA9IGxldmVscyhzb2JqX3RyYWokY2x1c3Rlcl90eXBlKSkgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGNvbG9yX21hcmtlcnNbYygiSUJMIiwgIk9SUyIsICJIRlNDIildLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIklCTCIsICJPUlMiLCAiSEZTQyIsIE5BKSwgbmEudmFsdWUgPSBiZ19jb2xvcikgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKClByb2plY3QgbmFtZSA6CgpgYGB7ciBmaWdfdHJhal9wcm9qZWN0X25hbWUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQojIFJhbmRvbSBvcmRlcgpzZXQuc2VlZCgxMjM0KQpybmRfb3JkZXIgPSBzYW1wbGUoY29sbmFtZXMoc29ial90cmFqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqX3RyYWopKQoKIyBFeHRyYWN0IGNvb3JkaW5hdGVzCmNlbGxzX2Nvb3JkID0gc29ial90cmFqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIkRpbTEiLCAiRGltMiIpKQpjZWxsc19jb29yZCRzYW1wbGVfdHlwZSA9IHNvYmpfdHJhaiRzYW1wbGVfdHlwZQpjZWxsc19jb29yZCA9IGNlbGxzX2Nvb3JkW29yZGVyKHNvYmpfdHJhaiRzYW1wbGVfdHlwZSksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHNhbXBsZV90eXBlKSkgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoc2l6ZSA9IDEuMikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKHNhbXBsZV90eXBlX2NvbG9ycykpICsKICBnZ3Bsb3QyOjp0aGVtZV92b2lkKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCgpDbHVzdGVyIHR5cGUgOgoKYGBge3IgZmlnX3RyYWpfY2x1c3Rlcl90eXBlLCBmaWcud2lkdGggPSAzLCBmaWcuaGVpZ2h0ID0gM30KU2V1cmF0OjpEaW1QbG90KHNvYmpfdHJhaiwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMC41LAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgY29scyA9IGNvbG9yX21hcmtlcnMpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArCiAgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKUHNldWRvdGltZSA6CgpgYGB7ciBmaWdfdHJhal9wc2V1ZG90aW1lLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX3RyYWosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJwc2V1ZG90aW1lIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSB2aXJpZGlzOjp2aXJpZGlzKG4gPSAxMDApKSArCiAgZ2dwbG90Mjo6bGltcyh4ID0gcmFuZ2Uoc29ial90cmFqQHJlZHVjdGlvbnNbW25hbWUyRF1dQGNlbGwuZW1iZWRkaW5nc1ssIDFdKSwKICAgICAgICAgICAgICAgIHkgPSByYW5nZShzb2JqX3RyYWpAcmVkdWN0aW9uc1tbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzWywgMl0pKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKQpgYGAKClBzZXVkb3RpbWUgd2l0aCBkeW5wbG90J3MgZnVuY3Rpb24gOgoKYGBge3IgZmlnX3RyYWpfcHNldWRvdGltZV9keW5wbG90LCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KZHlucGxvdDo6cGxvdF9kaW1yZWQodHJhamVjdG9yeSA9IG15X3RyYWosCiAgICAgICAgICAgICAgICAgICAgIGRpbXJlZCA9IHNvYmpfdHJhaltbbmFtZTJEXV1AY2VsbC5lbWJlZGRpbmdzLAogICAgICAgICAgICAgICAgICAgICAjIENlbGxzCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yX2NlbGxzID0gJ3BzZXVkb3RpbWUnLAogICAgICAgICAgICAgICAgICAgICBzaXplX2NlbGxzID0gMS42LAogICAgICAgICAgICAgICAgICAgICBib3JkZXJfcmFkaXVzX3BlcmNlbnRhZ2UgPSAwLAogICAgICAgICAgICAgICAgICAgICAjIFRyYWplY3RvcnkKICAgICAgICAgICAgICAgICAgICAgcGxvdF90cmFqZWN0b3J5ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgY29sb3JfdHJhamVjdG9yeSA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxfbWlsZXN0b25lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICBzaXplX21pbGVzdG9uZXMgPSAwLAogICAgICAgICAgICAgICAgICAgICBzaXplX3RyYW5zaXRpb25zID0gMSkKYGBgCgoKIyBNYXRyaXggY2VsbHMKCiMjIFNldHRpbmdzCgpXZSBsb2FkIHRoZSBpbW11bmUgY2VsbHMgZGF0YXNldCA6CgpgYGB7ciBzb2JqX21hdH0Kc29ial9tYXQgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vNV96b29tX21hdHJpeC9tYXRyaXhfc29iai5yZHMiKSkKc29ial9tYXQKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF9tYXR9Cm5hbWUyRCA9ICJoYXJtb255XzE5X3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfbWF0fQpsaXN0X3Jlc3VsdHMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi80X3pvb20vNV96b29tX21hdHJpeC9tYXRyaXhfbGlzdF9yZXN1bHRzLnJkcyIpKQoKbGFwcGx5KGxpc3RfcmVzdWx0cywgRlVOID0gbmFtZXMpCmBgYAoKIyMgUHJlcGFyYXRpb24KCldlIGRlZmluZWQgZmFjdG9yIGxldmVscyBmb3IgY2x1c3RlciB0eXBlIDoKCmBgYHtyIHNvYmpfbWF0X2NsdXN0ZXJfdHlwZX0Kc29ial9tYXQkY2x1c3Rlcl90eXBlID0gZmFjdG9yKHNvYmpfbWF0JGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIm1lZHVsbGEiLCAiY29ydGV4IiwgIklSUyIsICJjdXRpY2xlIikpCgp0YWJsZShzb2JqX21hdCRjbHVzdGVyX3R5cGUsIHNvYmpfbWF0JHNhbXBsZV9pZGVudGlmaWVyKQpgYGAKCgojIyBGaWd1cmVzCgpNYXRyaXggY2VsbHMgb24gdGhlIGZ1bGwgYXRsYXMgOgoKYGBge3IgZmlnX21hdF9sb2NhdGlvbiwgZmlnLndpZHRoID0gMiwgZmlnLmhlaWdodCA9IDJ9CnNvYmokaXNfbWF0cml4ID0gKGNvbG5hbWVzKHNvYmopICVpbiUgY29sbmFtZXMoc29ial9tYXQpKQoKU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRF9hdGxhcywgcHQuc2l6ZSA9IDAuMDAwMDAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiaXNfbWF0cml4Iiwgb3JkZXIgPSAiVFJVRSIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhjYXRlZ29yeV9jb2xvcltbIm1hdHJpeCJdXSwgYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKFRSVUUsIEZBTFNFKSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiTWF0cml4IGNlbGxzIiwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKG5jb2woc29ial9tYXQpLCAiIGNlbGxzIikpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgU2V1cmF0OjpOb0F4ZXMoKSArIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXIgdHlwZSA6CgpgYGB7ciBmaWdfbWF0X2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqX21hdCwgcmVkdWN0aW9uID0gbmFtZTJELCBwdC5zaXplID0gMSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXJpbmcgOgoKYGBge3IgZmlnX21hdF9jbHVzdGVyaW5nLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmpfbWF0LCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgbGFiZWwgPSBUUlVFKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkJhcnBsb3Qgd2l0aCBwcm9wb3J0aW9uIDoKCmBgYHtyIGZpZ19tYXRfYmFycGxvdF9jYXRlZ29yeSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAzLjV9CnF1YW50aWZfdG90YWwgPSB0YWJsZShzb2JqJHNhbXBsZV9pZGVudGlmaWVyKSAlPiUKICBhcy5kYXRhLmZyYW1lLnRhYmxlKCkgJT4lCiAgYGNvbG5hbWVzPC1gKGMoIlNhbXBsZSIsICJuYl9jZWxscyIpKQoKIyBOdW1iZXIgb2YgbWF0cml4IGNlbGxzCnF1YW50aWZfaW1tdW5lID0gdGFibGUoc29ial9tYXQkc2FtcGxlX2lkZW50aWZpZXIsIHNvYmpfbWF0JGNsdXN0ZXJfdHlwZSkgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSgpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbFR5cGUiLCAiTnVtYmVyIikpICU+JQogICMgcHJvcG9ydGlvbiBmb3IgdGhlIHdob2xlIGRhdGFzZXQKICBkcGx5cjo6bGVmdF9qb2luKC4sIHF1YW50aWZfdG90YWwsIGJ5ID0gIlNhbXBsZSIpICU+JQogIGRwbHlyOjptdXRhdGUocHJvcF9jZWxscyA9IDEwMCpyb3VuZChOdW1iZXIvbmJfY2VsbHMsNCkpICU+JQogIGBjb2xuYW1lczwtYChjKCJTYW1wbGUiLCAiQ2VsbFR5cGUiLCAiTnVtYmVyIiwgIlRvdGFsIiwgIlByb3BvcnRpb24iKSkKCmdncGxvdDI6OmdncGxvdChkYXRhID0gcXVhbnRpZl9pbW11bmUsCiAgICAgICAgICAgICAgICBhZXMoeCA9IFNhbXBsZSwgeSA9IFByb3BvcnRpb24sIGZpbGwgPSBDZWxsVHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNyksCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yNSwgd2lkdGggPSAwLjcpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB1bmxpc3QoY29sb3JfbWFya2Vyc1tsZXZlbHMoc29ial9tYXQkY2x1c3Rlcl90eXBlKV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IG5hbWVzKGNvbG9yX21hcmtlcnNbbGV2ZWxzKHNvYmpfbWF0JGNsdXN0ZXJfdHlwZSldKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkNlbGwgVHlwZSIpICsKICAjIFRoZW1lCiAgZ2dwbG90Mjo6c2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgbWF4KHF1YW50aWZfaW1tdW5lJFByb3BvcnRpb24pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZ2dwbG90Mjo6ZXhwYW5zaW9uKGFkZCA9IGMoMCwgMSkpKSArCiAgZ2dwbG90Mjo6bGFicyh5ID0gIiUgb2YgbWF0cml4IGNlbGxzIGJ5IHNhbXBsZSIpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAibGlnaHRncmF5IiksCiAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAwLjUpLAogICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJsaWdodGdyYXkiLCBzaXplID0gMC41KSwKICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAibGlnaHRncmF5Iiwgc2l6ZSA9IDAuNSkpCmBgYAoKR1NFQSBwbG90cyA6CgpgYGB7ciBmaWdfbWF0X2dzZWFfMSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIkdPQ0NfS0VSQVRJTl9GSUxBTUVOVCIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJGNvcnRleCRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkY29ydGV4JGdzZWEsCiAgICAgICAgICAgICAgICAgICAgICBnZW5lU2V0SUQgPSB0aGVfZ3NfbmFtZSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSB0aGVfZ3NfbmFtZSwKICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gdGhlX3N1YnRpdGxlKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGdndGV4dDo6ZWxlbWVudF9tYXJrZG93bihoanVzdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMCkpCmBgYAoKYGBge3IgZmlnX21hdF9nc2VhXzIsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA0fQp0aGVfZ3NfbmFtZSA9ICJHT0JQX0lOVEVSTUVESUFURV9GSUxBTUVOVF9PUkdBTklaQVRJT04iIAp0aGVfY29udGVudCA9IGxpc3RfcmVzdWx0cyRjb3J0ZXgkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRwdmFsdWUgOiAiLCByb3VuZCh0aGVfY29udGVudCRwdmFsdWUsIDQpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0c2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJGNvcnRleCRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKClZpb2xpbiBwbG90CgpgYGB7ciBmaWdfbWF0X3Zsbl9zcGxpdCwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpmZWF0dXJlc19vaSA9IGMoIktSVDMyIikKCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlc19vaSwgRlVOID0gZnVuY3Rpb24oZmVhdHVyZSkgewogIGZlYXR1cmVfZXhwciA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfbWF0LCBmZWF0dXJlKVssIDFdCiAgeV9tYXggPSBtYXgoZmVhdHVyZV9leHByKSoxLjEKICAKICAjIERhdGFmcmFtZSBzdGF0CiAgcG9wX29pID0gbGV2ZWxzKHNvYmpfbWF0JGNsdXN0ZXJfdHlwZSkKICAKICBkZl9zdGF0ID0gbGFwcGx5KGMoMTpsZW5ndGgocG9wX29pKSksIEZVTiA9IGZ1bmN0aW9uKGkpIHsKICAgIHBvcCA9IHBvcF9vaVtpXQogICAgCiAgICAjIFN0YXQKICAgIGZlYXR1cmVfaHMgPSBmZWF0dXJlX2V4cHJbc29ial9tYXQkc2FtcGxlX3R5cGUgPT0gIkhTIiAmIHNvYmpfbWF0JGNsdXN0ZXJfdHlwZSA9PSBwb3BdCiAgICBmZWF0dXJlX2hkID0gZmVhdHVyZV9leHByW3NvYmpfbWF0JHNhbXBsZV90eXBlID09ICJIRCIgJiBzb2JqX21hdCRjbHVzdGVyX3R5cGUgPT0gcG9wXQogICAgZmVhdHVyZV9oc192c19oZCA9IHN0YXRzOjp0LnRlc3QoZmVhdHVyZV9ocywgZmVhdHVyZV9oZCkKICAgIHNpZ25pZmljYW5jZSA9IHNpZ25pZmljYW50KGZlYXR1cmVfaHNfdnNfaGQkcC52YWx1ZSkKICAgIAogICAgIyBPdXRwdXQgKHgsIHBvcCwgc2lnbmlmKQogICAgcm93X2kgPSBjKGksIHBvcCwgc2lnbmlmaWNhbmNlKQogICAgCiAgICByZXR1cm4ocm93X2kpCiAgfSkgJT4lIGRvLmNhbGwocmJpbmQuZGF0YS5mcmFtZSwgLikgJT4lCiAgICBgY29sbmFtZXM8LWAoYygieCIsICJwb3AiLCAic2lnbmlmIikpICU+JQogICAgZHBseXI6Om11dGF0ZSh4ID0gYXMubnVtZXJpYyh4KSkKICAKICAjIFBsb3QKICBwID0gU2V1cmF0OjpWbG5QbG90KHNvYmpfbWF0LCBmZWF0dXJlcyA9IGZlYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgc3BsaXQuYnkgPSAic2FtcGxlX3R5cGUiKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzYW1wbGVfdHlwZV9jb2xvcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhzYW1wbGVfdHlwZV9jb2xvcnMpKSArCiAgICAjIFN0YXQKICAgIGdncGxvdDI6Omdlb21fc2VnbWVudChkYXRhID0gZGZfc3RhdCwgaW5oZXJpdC5hZXMgPSBGQUxTRSwgc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSB4IC0gMC4yNSwgeGVuZCA9IHggKyAwLjI1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0geV9tYXgsIHllbmQgPSB5X21heCkgKwogICAgZ2dwbG90Mjo6Z2VvbV9sYWJlbChkYXRhID0gZGZfc3RhdCwgaW5oZXJpdC5hZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcG9wLCBsYWJlbCA9IHNpZ25pZiksCiAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB5X21heCoxLjA1LAogICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsIGxhYmVsLnNpemUgPSAwKSArCiAgICAjIFRoZW1lCiAgICBnZ3Bsb3QyOjpsaW1zKHkgPSBjKDAsIHlfbWF4KjEuMSkpICsKICAgIGdncGxvdDI6OnRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCiAgCiAgcmV0dXJuKHApCn0pCgpuYW1lcyhwbG90X2xpc3QpID0gZmVhdHVyZXNfb2kKcGxvdF9saXN0CmBgYAoKIyBPRVAwMDIzMjEgZGF0YXNldAoKIyMgU2V0dGluZ3MKCldlIGxvYWQgdGhlIGRhdGFzZXQgY29udGFpbmluZyBhbGwgY2VsbHMgOgoKYGBge3IgbG9hZF9zb2JqX3d1fQpzb2JqID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvNV93dS8zX2NvbWJpbmVkL3d1X3NvYmoucmRzIikpCnNvYmoKYGBgCgpUaGlzIGlzIHRoZSBwcm9qZWN0aW9uIG5hbWUgdG8gdmlzdWFsaXplIGNlbGxzIDoKCmBgYHtyIGFsbF9zb2JqX25hbWUyRF93dX0KbmFtZTJEID0gImhhcm1vbnlfMzhfdHNuZSIKbmFtZTJEX2F0bGFzID0gbmFtZTJECmBgYAoKVGhlc2UgYXJlIGFsbCB0aGUgc2FtcGxlcyBhbmFseXplZCA6CgpgYGB7ciBzYW1wbGVfaW5mb193dSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtaGlkZScsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAzfQpzYW1wbGVfaW5mbyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvMV9tZXRhZGF0YS93dV9zYW1wbGVfaW5mby5yZHMiKSkKCiMgTmIgY2VsbHMgYnkgZGF0YXNldAp0b19wbG90ID0gdGFibGUoc29iaiRzYW1wbGVfaWRlbnRpZmllcikgJT4lCiAgYXMuZGF0YS5mcmFtZS50YWJsZSguLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICU+JQogIGBjb2xuYW1lczwtYChjKCJzYW1wbGVfaWRlbnRpZmllciIsICJuYl9jZWxscyIpKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKHggPSAuLCB5ID0gc2FtcGxlX2luZm8sIGJ5ID0gInNhbXBsZV9pZGVudGlmaWVyIikgCgojIHBhdGNod29yawpwbG90X2xpc3QgPSBhcXVhcml1czo6ZmlnX3Bsb3RfZ2IodG9fcGxvdCwgdGl0bGUgPSAiQXZhaWxhYmxlIGRhdGFzZXRzIikKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCkgKwogIHBhdGNod29yazo6cGxvdF9sYXlvdXQoZGVzaWduID0gIkFcbkIiLCBoZWlnaHRzID0gYygwLjEsNSkpICYKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLCBzaXplID0gMTUpKQpgYGAKCldlIGRlZmluZSBjbHVzdGVyIHR5cGUgYW5kIGNsdXN0ZXIgY2F0ZWdvcnkgOgoKYGBge3IgY2x1c3Rlcl90eXBlX2NhdGVnb3J5X3d1LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNX0Kc29iaiRjZWxsX3R5cGUgPSBzb2JqJGNlbGxfdHlwZSAlPiUKICBhcy5jaGFyYWN0ZXIoKSAlPiUKICBmYWN0b3IoLiwgbGV2ZWxzID0gbmFtZXMoY29sb3JfbWFya2VycykpCgpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqJGNlbGxfdHlwZSwgc29iaiRzZXVyYXRfY2x1c3RlcnMpICU+JQogIHByb3AudGFibGUoLiwgbWFyZ2luID0gMikgJT4lCiAgYXBwbHkoLiwgMiwgd2hpY2gubWF4KQpjbHVzdGVyX3R5cGUgPSBzZXROYW1lcyhubSA9IG5hbWVzKGNsdXN0ZXJfdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyhzb2JqJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmokY2x1c3Rlcl90eXBlID0gY2x1c3Rlcl90eXBlW3NvYmokc2V1cmF0X2NsdXN0ZXJzXQpzb2JqJGNsdXN0ZXJfdHlwZSA9IGZhY3Rvcihzb2JqJGNsdXN0ZXJfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gbGV2ZWxzKHNvYmokY2VsbF90eXBlKSkgJT4lCiAgYmFzZTo6ZHJvcGxldmVscygpCnNvYmokY2x1c3Rlcl9jYXRlZ29yeSA9IGN1c3RvbV9vcmRlcl9jZWxsX3R5cGVbc29iaiRjbHVzdGVyX3R5cGUsICJjZWxsX2NhdGVnb3J5Il0Kc29iaiRjbHVzdGVyX2NhdGVnb3J5ID0gZmFjdG9yKHNvYmokY2x1c3Rlcl9jYXRlZ29yeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IG5hbWVzKGNhdGVnb3J5X2NvbG9yKSkKYGBgCgoKIyMgR2xvYmFsIGZpZ3VyZXMKClByb2plY3QgbmFtZSA6CgpgYGB7ciBmaWdzMl9zYW1wbGVfaWRlbnRpZmllciwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CiMgUmFuZG9tIG9yZGVyCnNldC5zZWVkKDEyMzQpCnJuZF9vcmRlciA9IHNhbXBsZShjb2xuYW1lcyhzb2JqKSwgcmVwbGFjZSA9IEZBTFNFLCBzaXplID0gbmNvbChzb2JqKSkKCiMgRXh0cmFjdCBjb29yZGluYXRlcwpjZWxsc19jb29yZCA9IHNvYmpAcmVkdWN0aW9uc1tbbmFtZTJEX2F0bGFzXV1AY2VsbC5lbWJlZGRpbmdzICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiRGltMSIsICJEaW0yIikpCmNlbGxzX2Nvb3JkJHByb2plY3RfbmFtZSA9IHNvYmokcHJvamVjdF9uYW1lCmNlbGxzX2Nvb3JkID0gY2VsbHNfY29vcmRbKHJuZF9vcmRlciksIF0KCiMgUGxvdApnZ3Bsb3QyOjpnZ3Bsb3QoY2VsbHNfY29vcmQsIGFlcyh4ID0gRGltMSwgeSA9IERpbTIsIGNvbCA9IHByb2plY3RfbmFtZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHNpemUgPSAwLjUpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNhbXBsZV9pbmZvJHByb2plY3RfbmFtZSkgKwogIGdncGxvdDI6OnRoZW1lX3ZvaWQoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSwKICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKQ2VsbCB0eXBlIGFubm90YXRpb24gOgoKYGBge3IgZmlnczJfY2VsbF90eXBlLCBmaWcud2lkdGggPSA0LCBmaWcuaGVpZ2h0ID0gNH0KU2V1cmF0OjpEaW1QbG90KHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNlbGxfdHlwZSIsIGNvbHMgPSBjb2xvcl9tYXJrZXJzKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCkNsdXN0ZXIgdHlwZSBhbm5vdGF0aW9uIDoKCmBgYHtyIGZpZ3MyX2NsdXN0ZXJfdHlwZSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9ClNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIHB0LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgpHZW5lIGV4cHJlc3Npb24gdG8gYXNzZXNzIGFubm90YXRpb24gOgoKYGBge3IgZmlnczJfZ2VuZV9jYXRlZ29yeSwgZmlnLndpZHRoID0gNCwgZmlnLmhlaWdodCA9IDR9CmdlbmVzID0gYygiUFRQUkMiLCAiTVNYMiIsICJLUlQxNCIpCm5hbWVzKGdlbmVzKSA9IGMoImltbXVuZSBjZWxscyIsICJtYXRyaXggY2VsbHMiLCAibm9uLW1hdHJpeCBjZWxscyIpCgpwbG90X2xpc3QgPSBsYXBwbHkoYygxOmxlbmd0aChnZW5lcykpLCBGVU4gPSBmdW5jdGlvbihnZW5lX2lkKSB7CiAgZ2VuZSA9IGdlbmVzW1tnZW5lX2lkXV0KICBwb3AgPSBuYW1lcyhnZW5lcylbZ2VuZV9pZF0KICAKICBzb2JqJG15X2dlbmUgPSBTZXVyYXQ6OkZldGNoRGF0YShzb2JqLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqLCBmZWF0dXJlcyA9ICJteV9nZW5lIiwgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzKSArCiAgICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gYXF1YXJpdXM6OmNvbG9yX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDEwLCBieSA9IDIuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygibWluIiwgcmVwKCIiLCAzKSwgIm1heCIpKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gZ2VuZSkgKyAKICAgICMgc3VidGl0bGUgPSBwb3ApICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsCiAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTcpLAogICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE1KSwKICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIFNldXJhdDo6Tm9BeGVzKCkKfSkKCnBsb3RfbGlzdApgYGAKCkRvdHBsb3QgOgoKYGBge3IgZmlnczJfZG90cGxvdCwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDUsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpjdXN0b21fb3JkZXJfY2VsbF90eXBlID0gY3VzdG9tX29yZGVyX2NlbGxfdHlwZVtsZXZlbHMoc29iaiRjbHVzdGVyX3R5cGUpLCBjKCJjZWxsX3R5cGUiLCAiY2VsbF9jYXRlZ29yeSIpXQoKcGxvdF9saXN0ID0gU2V1cmF0OjpEb3RQbG90KHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIlBUUFJDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0QzRSIsICJDRDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDRDIwNyIsICJBSUYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICJQUkRNMSIsICJLUlQ4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1TWDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLUlQzMiIsICJLUlQzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktSVDMxIiwgIlBSUjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCQU1CSSIsICJBTERIMUEzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUNzEiLCAiS1JUNzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUT1AyQSIsICJNQ001IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMTQiLCAiQ1hDTDE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMTUiLCAiQ09MMTdBMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRJTzIiLCAiVENFQUwyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1JUMTYiLCAiS1JUNkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTUElOSzUiLCAiTFk2RCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIiwgc2NhbGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUuYnkgPSAicmFkaXVzIiwgc2NhbGUubWluID0gTkEsIHNjYWxlLm1heCA9IE5BKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IGFxdWFyaXVzOjpjb2xvcl9nZW5lKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgICAgICAgICAgICAgIyBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgIGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIsCiAgICAgICAgICAgICAgICAgbGVnZW5kLmJveC5tYXJnaW4gPSBtYXJnaW4oMCwyNSwwLDApLAogICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDExLCBjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMC41LDAsMCksICJjbSIpKQoKcCA9IGdncGxvdDI6OmdncGxvdChjdXN0b21fb3JkZXJfY2VsbF90eXBlLCBhZXMoeSA9IGNlbGxfdHlwZSwgeCA9IDApKSArCiAgZ2dwbG90Mjo6Z2VvbV9wb2ludChzaXplID0gMCkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeSA9IDAuNSwgeWVuZCA9IDIuNSwgeCA9IDAsIHhlbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJpbW11bmUgY2VsbHMiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeSA9IDIuNSwgeWVuZCA9IDcuNSwgeCA9IDAsIHhlbmQgPSAwKSwgc2l6ZSA9IDYsIGNvbCA9IGNhdGVnb3J5X2NvbG9yWyJtYXRyaXgiXSkgKwogIGdncGxvdDI6Omdlb21fc2VnbWVudChhZXMoeSA9IDcuNSwgeWVuZCA9IDExLjUsIHggPSAwLCB4ZW5kID0gMCksIHNpemUgPSA2LCBjb2wgPSBjYXRlZ29yeV9jb2xvclsibm9uIG1hdHJpeCJdKSArCiAgZ2dwbG90Mjo6c2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgbGltaXRzID0gYygwLDApKSArCiAgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYygpICsKICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAsMCwwLjUpLCAiY20iKSkKCnBsb3RfbGlzdCA9IHBhdGNod29yazo6d3JhcF9wbG90cyhwLCBwbG90X2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMSwgd2lkdGhzID0gYygxLCAyNSkpCnBsb3RfbGlzdApgYGAKCiMjIElCTCBhbmQgT1JTIGRhdGFzZXQKCldlIGxvYWQgdGhlIElCTCArIE9SUyBkYXRhc2V0IDoKCmBgYHtyIHNvYmpfaWJsb3JzX3d1fQpzb2JqX2libG9ycyA9IHJlYWRSRFMocGFzdGUwKGRhdGFfZGlyLCAiLzVfd3UvNF9pYmxfb3JzL2libG1vcnNfc29iai5yZHMiKSkKc29ial9pYmxvcnMKYGBgCgoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBuYW1lIHRvIHZpc3VhbGl6ZSBjZWxscyA6CgpgYGB7ciBzb2JqX25hbWUyRF9pYmxvcnNfd3V9Cm5hbWUyRCA9ICJoYXJtb255XzIwX3RzbmUiCmBgYAoKVG8gcmVwcmVzZW50IHJlc3VsdHMgZnJvbSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiwgd2UgbG9hZCB0aGUgYW5hbHlzZXMgcmVzdWx0cyA6CgpgYGB7ciBsaXN0X3Jlc3VsdHNfaWJsb3JzX3d1fQpsaXN0X3Jlc3VsdHMgPSByZWFkUkRTKHBhc3RlMChkYXRhX2RpciwgIi81X3d1LzRfaWJsX29ycy9pYmxtb3JzX2xpc3RfcmVzdWx0cy5yZHMiKSkKCmxhcHBseShsaXN0X3Jlc3VsdHMsIEZVTiA9IG5hbWVzKQpgYGAKCldlIGRlZmluZWQgY2x1c3RlciB0eXBlIDoKCmBgYHtyIGNsdXN0ZXJfdHlwZV9pYmxvcnNfd3UsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1fQpjbHVzdGVyX3R5cGUgPSB0YWJsZShzb2JqX2libG9ycyRjZWxsX3R5cGUsIHNvYmpfaWJsb3JzJHNldXJhdF9jbHVzdGVycykgJT4lCiAgcHJvcC50YWJsZSguLCBtYXJnaW4gPSAyKSAlPiUKICBhcHBseSguLCAyLCB3aGljaC5tYXgpCmNsdXN0ZXJfdHlwZSA9IHNldE5hbWVzKG5tID0gbmFtZXMoY2x1c3Rlcl90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzKHNvYmpfaWJsb3JzJGNlbGxfdHlwZSlbY2x1c3Rlcl90eXBlXSkKCnNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfdHlwZVtzb2JqX2libG9ycyRzZXVyYXRfY2x1c3RlcnNdCnNvYmpfaWJsb3JzJGNsdXN0ZXJfdHlwZSA9IGZhY3Rvcihzb2JqX2libG9ycyRjbHVzdGVyX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJJQkwiLCAiT1JTIikpCmBgYAoKIyMgSUJMICsgT1JTIGZpZ3VyZQoKSUJMICsgT1JTIG9uIHRoZSBmdWxsIGF0bGFzIDoKCmBgYHtyIGZpZ3MyX2libG9yc19sb2NhdGlvbiwgZmlnLndpZHRoID0gMS41LCBmaWcuaGVpZ2h0ID0gMS41fQpzb2JqJGNlbGxfYmMgPSBjb2xuYW1lcyhzb2JqKQpzb2JqX2libG9ycyRjZWxsX2JjID0gY29sbmFtZXMoc29ial9pYmxvcnMpCnNvYmokaXNfaWJsb3JzID0gZHBseXI6OmxlZnRfam9pbihzb2JqQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAicGVyY2VudC5tdCIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfaWJsb3JzQG1ldGEuZGF0YVssIGMoImNlbGxfYmMiLCAiY2x1c3Rlcl90eXBlIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAiY2VsbF9iYyIpWywgImNsdXN0ZXJfdHlwZSJdCgpTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgcmVkdWN0aW9uID0gbmFtZTJEX2F0bGFzLCBwdC5zaXplID0gMC4wMDAwMDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJpc19pYmxvcnMiLCBvcmRlciA9IEZBTFNFKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoY29sb3JfbWFya2Vyc1tjKCJJQkwiLCAiT1JTIildLCBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIklCTCIsICJPUlMiLCBOQSksIG5hLnZhbHVlID0gYmdfY29sb3IpICsKICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsgU2V1cmF0OjpOb0xlZ2VuZCgpCmBgYAoKQ2x1c3RlciB0eXBlIDoKCmBgYHtyIGZpZ3MyX2libG9yc19jbHVzdGVyX3R5cGUsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSA0fQpTZXVyYXQ6OkRpbVBsb3Qoc29ial9pYmxvcnMsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDEsCiAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJjbHVzdGVyX3R5cGUiLCBjb2xzID0gY29sb3JfbWFya2VycykgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICBTZXVyYXQ6Ok5vQXhlcygpICsKICBTZXVyYXQ6Ok5vTGVnZW5kKCkKYGBgCgoKREUgZ2VuZXMgYmV0d2VlbiBJQkwgYW5kIE9SUyA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfZGVfcG9wLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KbWFyayA9IGxpc3RfcmVzdWx0cyRJQkxfdnNfT1JTJG1hcmsKbWFyayRnZW5lX25hbWUgPSByb3duYW1lcyhtYXJrKQptYXJrX2xhYmVsID0gcmJpbmQoCiAgIyB1cC1yZWd1bGF0ZWQgaW4gSUJMCiAgbWFyayAlPiUgZHBseXI6OnRvcF9uKC4sIG4gPSAyMCwgd3QgPSBhdmdfbG9nRkMpLAogICMgdXAtcmVndWxhdGVkIGluIE9SUwogIG1hcmsgJT4lIGRwbHlyOjp0b3BfbiguLCBuID0gMjAsIHd0ID0gLWF2Z19sb2dGQyksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBJQkwKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IChwY3QuMSAtIHBjdC4yKSksCiAgIyByZXByZXNlbnRhdGl2ZSBhbmQgc2VsZWN0aXZlIGZvciBPUlMKICBtYXJrICU+JSBkcGx5cjo6dG9wX24oLiwgbiA9IDIwLCB3dCA9IC0ocGN0LjEgLSBwY3QuMikpKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKQptYXJrX2xhYmVsID0gbWFya19sYWJlbFshZ3JlcGwocm93bmFtZXMobWFya19sYWJlbCksIHBhdHRlcm4gPSAiXk1UIiksIF0KCmF2Z19sb2dGQ19yYW5nZSA9IHNldE5hbWVzKGMobWluKG1hcmtfbGFiZWwkYXZnX2xvZ0ZDKSwgLTEsIDAsIDEsIG1heChtYXJrX2xhYmVsJGF2Z19sb2dGQykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBubSA9IGMoImRvZGdlcmJsdWU0IiwgImRvZGdlcmJsdWUzIiwgIiNCN0I3QjciLCAiZmlyZWJyaWNrMyIsICJmaXJlYnJpY2s0IikpCgoKZ2dwbG90Mjo6Z2dwbG90KG1hcmssIGFlcyh4ID0gcGN0LjEsIHkgPSBwY3QuMiwgY29sID0gYXZnX2xvZ0ZDKSkgKwogIGdncGxvdDI6Omdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbHR5ID0gMikgKwogIGdncGxvdDI6Omdlb21fcG9pbnQoKSArCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbChkYXRhID0gbWFya19sYWJlbCwgbWF4Lm92ZXJsYXBzID0gSW5mLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBwY3QuMSwgeSA9IHBjdC4yLCBsYWJlbCA9IGdlbmVfbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAzLjUsIGxhYmVsLnNpemUgPSBOQSkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJFbnJpY2hlZCBpbiBJQkwiLAogICAgICAgICAgICAgICAgeSA9ICJFbnJpY2hlZCBpbiBPUlMiKSArCiAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IG5hbWVzKGF2Z19sb2dGQ19yYW5nZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHNjYWxlczo6cmVzY2FsZSh1bm5hbWUoYXZnX2xvZ0ZDX3JhbmdlKSkpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKR1NFQSBwbG90IDoKCmBgYHtyIGZpZ3MyX2libG9yc19rZXJhdGluaXphdGlvbiwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CnRoZV9nc19uYW1lID0gIlJFQUNUT01FX0tFUkFUSU5JWkFUSU9OIiAKdGhlX2NvbnRlbnQgPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhQHJlc3VsdCAlPiUKICBkcGx5cjo6ZmlsdGVyKElEID09IHRoZV9nc19uYW1lKQp0aGVfc3VidGl0bGUgPSBwYXN0ZTAoIlxuTkVTIDogIiwgcm91bmQodGhlX2NvbnRlbnQkTkVTLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICJcdHxcdHB2YWx1ZSA6ICIsIHJvdW5kKHRoZV9jb250ZW50JHB2YWx1ZSwgNCksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRzZXQgc2l6ZSA6ICIsIHRoZV9jb250ZW50JHNldFNpemUsICIgZ2VuZXMiKQoKZW5yaWNocGxvdDo6Z3NlYXBsb3QyKHggPSBsaXN0X3Jlc3VsdHMkSUJMX3ZzX09SUyRnc2VhLAogICAgICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gdGhlX2dzX25hbWUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gdGhlX2dzX25hbWUsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHRoZV9zdWJ0aXRsZSkgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMywgMywgNSwgMykpLAogICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTApKQpgYGAKCmBgYHtyIGZpZ3MyX2libG9yc19pZm5hLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNH0KdGhlX2dzX25hbWUgPSAiSEFMTE1BUktfSU5URVJGRVJPTl9HQU1NQV9SRVNQT05TRSIgCnRoZV9jb250ZW50ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYUByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihJRCA9PSB0aGVfZ3NfbmFtZSkKdGhlX3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKHRoZV9jb250ZW50JE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAiXHR8XHRwdmFsdWUgOiAiLCByb3VuZCh0aGVfY29udGVudCRwdmFsdWUsIDQpLAogICAgICAgICAgICAgICAgICAgICAgIlx0fFx0c2V0IHNpemUgOiAiLCB0aGVfY29udGVudCRzZXRTaXplLCAiIGdlbmVzIikKCmVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gbGlzdF9yZXN1bHRzJElCTF92c19PUlMkZ3NlYSwKICAgICAgICAgICAgICAgICAgICAgIGdlbmVTZXRJRCA9IHRoZV9nc19uYW1lKSArCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHRoZV9nc19uYW1lLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSB0aGVfc3VidGl0bGUpICsKICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDMsIDMsIDUsIDMpKSwKICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZ2d0ZXh0OjplbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKYGBgCgpHZW5lcyBvZiBpbnRlcmVzdCA6CgpgYGB7ciBmaWdzMl9pYmxvcnNfZ2VuZXMsIGZpZy53aWR0aCA9IDIsIGZpZy5oZWlnaHQgPSAyfQpnZW5lcyA9IGMoIktSVDE2IiwgIkNPTDE3QTEiLCAiRFNUIiwgIktSVDZCIiwgIklMMVIyIiwgIldOVDMiLAogICAgICAgICAgIklGSTI3IiwgIkNYQ0wxNCIsICJJR0ZCUDMiLCAiS1JUMTUiLCAiQ0QyMDAiKQoKcGxvdF9saXN0ID0gbGFwcGx5KGMoMTpsZW5ndGgoZ2VuZXMpKSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9pZCkgewogIGdlbmUgPSBnZW5lc1tbZ2VuZV9pZF1dCiAgCiAgc29ial9pYmxvcnMkbXlfZ2VuZSA9IFNldXJhdDo6RmV0Y2hEYXRhKHNvYmpfaWJsb3JzLCBnZW5lKVssIDFdICU+JQogICAgYXF1YXJpdXM6OnJ1bl9yZXNjYWxlKC4sIG5ld19taW4gPSAwLCBuZXdfbWF4ID0gMTApCiAgCiAgU2V1cmF0OjpGZWF0dXJlUGxvdChzb2JqX2libG9ycywgZmVhdHVyZXMgPSAibXlfZ2VuZSIsIHJlZHVjdGlvbiA9IG5hbWUyRCwgcHQuc2l6ZSA9IDAuMjUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTAsIGJ5ID0gMi41KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJtaW4iLCByZXAoIiIsIDMpLCAibWF4IikpICsKICAgIGdncGxvdDI6OmxhYnModGl0bGUgPSBnZW5lKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShhc3BlY3QucmF0aW8gPSAxLAogICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE3KSwKICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNSksCiAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBTZXVyYXQ6Ok5vQXhlcygpCn0pCgpwbG90X2xpc3QKYGBgCgojIFN1cHBsZW1lbnRhcnkgdGFibGVzCgpJbiB0aGlzIHNlY3Rpb24sIHdlIHNhdmUgZmlsZXMgdG8gYXNzb2NpYXRlIHdpdGggdGhlIG1hbnVzY3JpcHQsIGFzIHN1cHBsZW1lbnRhcnkgdGFibGVzLgoKIyMgVGFibGUgUzIgKHBhY2thZ2UgdmVyc2lvbikKCldlIGxvYWQgdGhlIHRhYmxlIDoKCmBgYHtyIHRzMl9sb2FkfQpwYWNrYWdlX3ZlcnNpb24gPSByZWFkLnRhYmxlKHBhc3RlMCgiLiIsICIvZGF0YS9pbmZvX3RvX2luc3RhbGxfMjAyM18wNF8xNy50eHQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFKQpoZWFkKHBhY2thZ2VfdmVyc2lvbikKYGBgCgpDb2x1bW5zIGNvcnJlc3BvbmQgdG8gOgoKKiAqKm9yZGVyKiogOiBvcmRlciB0byBpbnN0YWxsIHBhY2thZ2Ugc3VjaCB0aGF0IG9uZSBuZXZlciBuZWVkIHRvIGluc3RhbGwgZGVwZW5kZW5jaWVzCiogKipwYWNrYWdlX25hbWUqKiA6IHBhY2thZ2UgbmFtZQoqICoqdmVyc2lvbioqIDogaW5zdGFsbGVkIHZlcnNpb24sIG9uIHRoZSBsb2NhbCBtYWNoaW5lCiogKip1cmwqKiA6IHRoZSBwYWNrYWdlIHdhcyBpbnN0YWxsZWQgaW4gdGhlIFNpbmd1bGFyaXR5IGNvbnRhaW5lciB1c2luZyB0aGlzIHVybAoKV2Ugc2F2ZSB0aGlzIHRhYmxlLCBleGNlcHQgdGhlIGxhc3QgY29sdW1uIChqdXN0IGZvciBtZSkgOgoKYGBge3IgdHMyX3NhdmV9Cm9wZW54bHN4Ojp3cml0ZS54bHN4KHBhY2thZ2VfdmVyc2lvblssIGMoMTo0KV0sCiAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoIi4iLCAiL2RhdGEvU3VwcGxlbWVudGFyeSBUYWJsZSAyLnhsc3giKSkKYGBgCgojIyBUYWJsZSBTMyAoY2VsbCB0eXBlIGFubm90YXRpb24pCgpXZSBsb2FkIHRoZSB0YWJsZSA6CgpgYGB7ciB0czNfbG9hZH0KY2VsbF9tYXJrZXJzID0gcmVhZFJEUyhwYXN0ZTAoZGF0YV9kaXIsICIvMV9tZXRhZGF0YS9oc19oZF9jZWxsX21hcmtlcnMucmRzIikpCmxlbmd0aHMoY2VsbF9tYXJrZXJzKQpgYGAKCldlIHNhdmUgdGhpcyB0YWJsZSwgZXhjZXB0IHRoZSBsYXN0IGNvbHVtbiAoanVzdCBmb3IgbWUpIDoKCmBgYHtyIHRzM19zYXZlfQpvcGVueGxzeDo6d3JpdGUueGxzeChjZWxsX21hcmtlcnMsCiAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAoIi4iLCAiL2RhdGEvU3VwcGxlbWVudGFyeSBUYWJsZSAzLnhsc3giKSkKYGBgCgoKIyBSIFNlc3Npb24KCmBgYHtyIHNlc3Npb25pbmZvLCBlY2hvID0gRkFMU0UsIGZvbGRfb3V0cHV0ID0gVFJVRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==